updating cycle close request feature to get approval from national dashboard instead auto from system
This commit is contained in:
1 parent
3a868cf1db
commit
e4dbfd0252
31 files changed
+1322
-182
No files matched your search
@@ -7,7 +7,7 @@ import type {
|
||||
ManualInput,
|
||||
} from '../types/api.ts';
|
||||
import type { DashboardReportPayload, DashboardReportType } from '../types/dashboardReport.ts';
|
||||
import { cycleDayForDate } from './format.ts';
|
||||
import { cycleDayForDate, cycleLastDayIndex, resolveCycleBoundDate } from './format.ts';
|
||||
|
||||
export type BuildInMemoryReportInput = {
|
||||
cycle: Cycle;
|
||||
@@ -48,9 +48,8 @@ function feedAccuracy(iot: number | null, manual: number | null): number | null
|
||||
export function buildInMemoryReport(input: BuildInMemoryReportInput): DashboardReportPayload {
|
||||
const { cycle } = input;
|
||||
// Day numbering stays 0-based (day 0 = start_date), but the report must never
|
||||
// run past the cycle end date. lastDay = start_date → end_date in days, which
|
||||
// is total_days - 1 for a contiguous cycle.
|
||||
const lastDay = cycleDayForDate(cycle.start_date, cycle.end_date);
|
||||
// run past the approved end (or soft bound while open).
|
||||
const lastDay = cycleLastDayIndex(cycle);
|
||||
const endCycleUpTo = Math.min(cycle.total_days, lastDay);
|
||||
const defaultUpTo =
|
||||
input.reportType === 'end_cycle'
|
||||
@@ -202,7 +201,7 @@ export function buildInMemoryReport(input: BuildInMemoryReportInput): DashboardR
|
||||
return {
|
||||
cycleId: String(cycle.id),
|
||||
cycleStart: cycle.start_date,
|
||||
cycleEnd: cycle.end_date,
|
||||
cycleEnd: cycle.end_date ?? resolveCycleBoundDate(cycle),
|
||||
kandangId: input.kandangId,
|
||||
kandangName: input.kandangName,
|
||||
area: input.area,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ChickenCounting, Cycle, KPI, ManualInput } from '../types/api.ts';
|
||||
import { addDaysIso, cycleDayForDate } from './format.ts';
|
||||
import { addDaysIso, cycleDayForDate, cycleLastDayIndex } from './format.ts';
|
||||
|
||||
export const COUNTING_ACCURACY_WARNING_THRESHOLD = 95;
|
||||
|
||||
@@ -38,7 +38,7 @@ function stockAkhirByDate(
|
||||
const manualByDate = new Map(manualInputs.map((m) => [m.date, m]));
|
||||
|
||||
const lastCycleDay = Math.max(0, (cycle.total_days || 1) - 1);
|
||||
const endDay = cycleDayForDate(cycle.start_date, cycle.end_date);
|
||||
const endDay = cycleLastDayIndex(cycle);
|
||||
const maxDay = Math.min(lastCycleDay, endDay);
|
||||
|
||||
const result = new Map<string, number>();
|
||||
|
||||
+45
-6
@@ -21,13 +21,15 @@ export function formatRatio(value: number, decimals = 3): string {
|
||||
return truncated.toFixed(decimals);
|
||||
}
|
||||
|
||||
export function formatDateId(iso: string): string {
|
||||
export function formatDateId(iso: string | null | undefined): string {
|
||||
if (!iso) return '—';
|
||||
const date = new Date(`${iso}T00:00:00`);
|
||||
if (Number.isNaN(date.getTime())) return iso;
|
||||
return date.toLocaleDateString('id-ID', { day: 'numeric', month: 'short', year: 'numeric' });
|
||||
}
|
||||
|
||||
export function formatDateRange(startIso: string, endIso: string): string {
|
||||
export function formatDateRange(startIso: string, endIso?: string | null): string {
|
||||
if (!endIso) return formatDateId(startIso);
|
||||
return `${formatDateId(startIso)} – ${formatDateId(endIso)}`;
|
||||
}
|
||||
|
||||
@@ -55,9 +57,46 @@ export function inclusiveDayCount(startIso: string, endIso: string): number {
|
||||
return Math.max(1, cycleDayForDate(startIso, endIso) + 1);
|
||||
}
|
||||
|
||||
/** Inclusive upper date for open cycles (null end_date) without producing NaN day math. */
|
||||
export function resolveCycleBoundDate(cycle: {
|
||||
start_date: string;
|
||||
end_date?: string | null;
|
||||
soft_end_date?: string | null;
|
||||
visible_through_date?: string | null;
|
||||
total_days?: number;
|
||||
}): string {
|
||||
if (cycle.end_date) return cycle.end_date;
|
||||
if (cycle.soft_end_date) return cycle.soft_end_date;
|
||||
if (cycle.visible_through_date) return cycle.visible_through_date;
|
||||
if (cycle.total_days != null && cycle.total_days > 0) {
|
||||
return addDaysIso(cycle.start_date, Math.max(cycle.total_days - 1, 0));
|
||||
}
|
||||
return todayIso();
|
||||
}
|
||||
|
||||
/** 0-based last day index from start → bound date (safe when end_date is null). */
|
||||
export function cycleLastDayIndex(cycle: {
|
||||
start_date: string;
|
||||
end_date?: string | null;
|
||||
soft_end_date?: string | null;
|
||||
visible_through_date?: string | null;
|
||||
total_days?: number;
|
||||
}): number {
|
||||
return cycleDayForDate(cycle.start_date, resolveCycleBoundDate(cycle));
|
||||
}
|
||||
|
||||
export type CycleStatusUi = 'active' | 'pending_close' | 'closed';
|
||||
|
||||
export function resolveCycleStatus(cycle: {
|
||||
end_date: string;
|
||||
visible_through_date: string;
|
||||
}): 'active' | 'closed' {
|
||||
return cycle.visible_through_date >= cycle.end_date ? 'closed' : 'active';
|
||||
status?: string;
|
||||
end_date?: string | null;
|
||||
visible_through_date?: string;
|
||||
}): CycleStatusUi {
|
||||
if (cycle.status === 'pending_close' || cycle.status === 'closed' || cycle.status === 'active') {
|
||||
return cycle.status;
|
||||
}
|
||||
if (cycle.end_date && cycle.visible_through_date && cycle.visible_through_date >= cycle.end_date) {
|
||||
return 'closed';
|
||||
}
|
||||
return 'active';
|
||||
}
|
||||
Reference in new issue
Block a user