fix: 修正归属工时舍入残差

This commit is contained in:
souplearn
2026-07-25 03:54:00 +08:00
parent 72acc16a09
commit 2084f834e3
2 changed files with 78 additions and 0 deletions
+57
View File
@@ -240,6 +240,58 @@ def _balance_minutes_to_targets(
_scale_bucket_minutes(minutes_by_item, key, scale)
def _round_bucket_cells_to_targets(
minutes_by_item: dict[int, dict[date, dict[str, float]]],
target_buckets: dict[str, float],
item_order: dict[int, int],
) -> None:
for bucket in SHIFT_BUCKETS:
cells: list[tuple[float, int, int, date]] = []
for item_id, rows_by_date in minutes_by_item.items():
for allocation_date, minutes in rows_by_date.items():
value = minutes.get(bucket, 0.0)
if value > 0:
cents_value = max(0.0, value * 100)
floor_cents = int(cents_value + 0.000000001)
cells.append((cents_value - floor_cents, floor_cents, item_order.get(item_id, 0), allocation_date))
minutes[bucket] = floor_cents / 100
else:
minutes[bucket] = 0.0
target_cents = int(round(round2(target_buckets.get(bucket, 0.0)) * 100))
floor_total = sum(cell[1] for cell in cells)
residual = target_cents - floor_total
if not cells or residual == 0:
continue
ordered = sorted(
cells,
key=lambda cell: (-cell[0], cell[2], cell[3].isoformat()),
)
if residual < 0:
ordered = sorted(
cells,
key=lambda cell: (cell[0], cell[2], cell[3].isoformat()),
)
cents_by_cell: dict[tuple[int, date], int] = {
(cell[2], cell[3]): cell[1] for cell in cells
}
steps = abs(residual)
for index in range(steps):
_, _, item_index, allocation_date = ordered[index % len(ordered)]
key = (item_index, allocation_date)
if residual > 0:
cents_by_cell[key] += 1
elif cents_by_cell[key] > 0:
cents_by_cell[key] -= 1
item_id_by_order = {order: item_id for item_id, order in item_order.items()}
for (item_index, allocation_date), cents in cents_by_cell.items():
item_id = item_id_by_order[item_index]
minutes_by_item[item_id][allocation_date][bucket] = cents / 100
def _items_by_device(items: list) -> dict[str, list]:
grouped: dict[str, list] = defaultdict(list)
for item in items:
@@ -367,6 +419,11 @@ def build_report_allocation_drafts(report, schedule: WorkScheduleConfig | None =
minutes_by_item = _range_minutes_by_item(report, items, schedule)
item_targets, bucket_targets = _allocation_targets(report, items, schedule)
_balance_minutes_to_targets(minutes_by_item, item_targets, bucket_targets)
_round_bucket_cells_to_targets(
minutes_by_item,
bucket_targets,
{id(item): index for index, item in enumerate(items)},
)
rows: list[ReportAllocationDraft] = []
for item in items: