|
|
@@ -9,6 +9,7 @@ import com.malk.service.aliwork.YDClient;
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
import org.junit.jupiter.params.ParameterizedTest;
|
|
|
import org.junit.jupiter.params.provider.ValueSource;
|
|
|
+import org.junit.jupiter.params.provider.CsvSource;
|
|
|
import com.malk.server.common.McException;
|
|
|
import org.springframework.test.util.ReflectionTestUtils;
|
|
|
|
|
|
@@ -32,6 +33,105 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
|
|
|
class TimeCardChangeApprovalWriteBackServiceTest {
|
|
|
+ @ParameterizedTest
|
|
|
+ @CsvSource({"false,8,2,6", "false,8,8,0", "false,8,12,0", "false,0,12,0",
|
|
|
+ "false,20,12,8", "false,-4,2,0", "true,8,2,6", "true,8,8,0",
|
|
|
+ "true,8,12,0", "true,0,12,0", "true,20,12,8", "true,-4,2,0"})
|
|
|
+ void shouldFloorHistoricalRequiredHoursAndKeepFullApprovedTransfer(
|
|
|
+ boolean repair, double current, double approved, double expected) {
|
|
|
+ WriteBackFixture fixture = positiveApprovalFixture(repair, approved);
|
|
|
+ fixture.db.get("required-target").put("numberField_mmd8onl6", current);
|
|
|
+ writePositiveApproval(fixture, repair);
|
|
|
+ assertHours(expected, fixture.db.get("required-target").get("numberField_mmd8onl6"));
|
|
|
+ assertHours(8d + approved, fixture.db.get("required-submit").get("numberField_mmd8onl6"));
|
|
|
+ assertHours(approved, fixture.db.get("shared-1").get("numberField_mmeakgi4"));
|
|
|
+ Map original = ((List<Map>) fixture.db.get("original-submit").get("tableField_mmczo634")).get(0);
|
|
|
+ assertHours(1d + approved, original.get("numberField_mmd80zae"));
|
|
|
+ assertHours(8d, original.get("numberField_mmczo637"));
|
|
|
+ String persisted = JSON.toJSONString(fixture.db);
|
|
|
+ writePositiveApproval(fixture, repair);
|
|
|
+ assertEquals(persisted, JSON.toJSONString(fixture.db));
|
|
|
+ }
|
|
|
+
|
|
|
+ @ParameterizedTest
|
|
|
+ @CsvSource({"20260920,true,0", "20260820,true,-4", "20260720,true,-4",
|
|
|
+ "20260920,false,-4"})
|
|
|
+ void shouldUsePersistedSubmissionMonthAndOnlyFloorHistoricalSources(
|
|
|
+ String submitDay, boolean source, double expected) {
|
|
|
+ WriteBackFixture fixture = writeBackFixture(true);
|
|
|
+ WorkHoursAdjustmentWriter writer = (WorkHoursAdjustmentWriter)
|
|
|
+ ReflectionTestUtils.getField(fixture.service, "adjustmentWriter");
|
|
|
+ writer.required("user-1", "required-only", 0, "20260818", -12d, source, submitDay);
|
|
|
+ assertHours(expected, fixture.db.get("required-target").get("numberField_mmd8onl6"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @ParameterizedTest
|
|
|
+ @ValueSource(booleans = {false, true})
|
|
|
+ void shouldRecoverClampedTargetAcrossReadbackFailureAndRestart(boolean repair) {
|
|
|
+ WriteBackFixture fixture = positiveApprovalFixture(repair, 12d);
|
|
|
+ YDClient client = (YDClient) ReflectionTestUtils.getField(fixture.service, "ydClient");
|
|
|
+ boolean[] failReadback = {true};
|
|
|
+ int[] targetWrites = {0};
|
|
|
+ when(client.queryData(any(), any())).thenAnswer(call -> {
|
|
|
+ YDParam request = call.getArgument(0);
|
|
|
+ if ("required-target".equals(request.getFormInstanceId()) && failReadback[0]
|
|
|
+ && targetWrites[0] > 0) throw new IllegalStateException("readback unavailable");
|
|
|
+ return queryFixture(fixture.db, request, call.getArgument(1));
|
|
|
+ });
|
|
|
+ when(client.operateData(any(), any())).thenAnswer(call -> {
|
|
|
+ YDParam request = call.getArgument(0);
|
|
|
+ fixture.db.get(request.getFormInstanceId()).putAll(JSON.parseObject(request.getUpdateFormDataJson()));
|
|
|
+ if ("required-target".equals(request.getFormInstanceId())) targetWrites[0]++;
|
|
|
+ return true;
|
|
|
+ });
|
|
|
+ assertThrows(IllegalStateException.class, () -> writePositiveApproval(fixture, repair));
|
|
|
+ assertHours(0d, fixture.db.get("required-target").get("numberField_mmd8onl6"));
|
|
|
+ assertHours(8d, fixture.db.get("required-submit").get("numberField_mmd8onl6"));
|
|
|
+ TimeCardChangeApprovalWriteBackService.clearLocalProgress("approval-1");
|
|
|
+ WorkHoursWriteBackJournal old = (WorkHoursWriteBackJournal)
|
|
|
+ ReflectionTestUtils.getField(fixture.service, "writeBackJournal");
|
|
|
+ WorkHoursWriteBackJournal restarted = new WorkHoursWriteBackJournal(
|
|
|
+ (java.nio.file.Path) ReflectionTestUtils.getField(old, "directory"));
|
|
|
+ WHConf conf = (WHConf) ReflectionTestUtils.getField(fixture.service, "whConf");
|
|
|
+ ReflectionTestUtils.setField(fixture.service, "writeBackJournal", restarted);
|
|
|
+ ReflectionTestUtils.setField(fixture.service, "adjustmentWriter",
|
|
|
+ new WorkHoursAdjustmentWriter(client, conf, null, restarted));
|
|
|
+ failReadback[0] = false;
|
|
|
+ writePositiveApproval(fixture, repair);
|
|
|
+ writePositiveApproval(fixture, repair);
|
|
|
+ assertEquals(1, targetWrites[0]);
|
|
|
+ assertHours(0d, fixture.db.get("required-target").get("numberField_mmd8onl6"));
|
|
|
+ assertHours(20d, fixture.db.get("required-submit").get("numberField_mmd8onl6"));
|
|
|
+ assertEquals("全部成功", fixture.db.get("approval-1").get("sync-status"));
|
|
|
+ }
|
|
|
+
|
|
|
+ private static WriteBackFixture positiveApprovalFixture(boolean repair, double approved) {
|
|
|
+ WriteBackFixture fixture = writeBackFixture(true);
|
|
|
+ Map child = ((List<Map>) fixture.db.get("shared-1").get("tableField_mmczo634")).get(0);
|
|
|
+ child.put("numberField_mmczo635", approved);
|
|
|
+ child.put("numberField_mmczo636", approved);
|
|
|
+ if (repair) child.remove("numberField_rtcChangeBaseBillable");
|
|
|
+ else child.put("numberField_rtcChangeBaseBillable", 0d);
|
|
|
+ fixture.db.get("shared-1").put("fill-type", repair ? "补填" : "变更");
|
|
|
+ Map approval = fixture.db.get("approval-1");
|
|
|
+ ((List<Map>) approval.get("tableField_mmae8t99")).get(0).put("numberField_mmacxew9", approved);
|
|
|
+ approval.put("submit-date", LocalDate.of(2026, 9, 20).atStartOfDay(ZoneId.of("Asia/Shanghai"))
|
|
|
+ .toInstant().toEpochMilli());
|
|
|
+ return fixture;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void writePositiveApproval(WriteBackFixture fixture, boolean repair) {
|
|
|
+ if (!repair) {
|
|
|
+ fixture.service.writeBack("approval-1", 1);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ RepairApprovalWriteBackService service = new RepairApprovalWriteBackService();
|
|
|
+ for (String field : new String[]{"ydClient", "whConf", "writeBackJournal", "adjustmentWriter"}) {
|
|
|
+ ReflectionTestUtils.setField(service, field, ReflectionTestUtils.getField(fixture.service, field));
|
|
|
+ }
|
|
|
+ service.writeBack("approval-1", 1);
|
|
|
+ }
|
|
|
+
|
|
|
@ParameterizedTest
|
|
|
@ValueSource(booleans = {false, true})
|
|
|
void shouldCarryProjectMetadataToNewAndExistingRows(boolean existing) {
|