Files
ApiCore/ApiCore-Backend/src/common/validation/update-dto-null-handling.spec.ts
T
Nick SzaunigandClaude Opus 5.5 8a31c42578
Backend Image / build-and-push (push) Successful in 6m39s
Frontend Image / build-and-push (push) Successful in 11m25s
feat: T6 wave 1 – compliance schema, permissions, lock order, backup
- Migrations (additive): veterinary medicines with lots and movement
  ledger, treatment medicine/lot/dose links, label particulars, authority
  notices, apiary restrictions, compliance settings and acknowledgements;
  notification category COMPLIANCE, added to non-empty Telegram lists.
- Permissions veterinary-medicines:* and compliance:* with role grants.
- LOCKABLE_TABLES: Apiary locks before Colony; T6 tables appended.
- Backup exports and restores the new tables in FK order; unsealed files
  skip them and report dropped medicine links; an open TSK draft in the
  target wins.
- describeNullContracts shared helper; Telegram backlog e2e no longer
  depends on the time of day.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-15 13:04:13 +02:00

371 lines
11 KiB
TypeScript

import { UpdateApiaryDto } from '../../apiaries/dto/update-apiary.dto';
import { UpdateAutomationRuleDto } from '../../automation/dto/update-automation-rule.dto';
import { UpdateBackupSettingsDto } from '../../backup/dto/update-backup-settings.dto';
import { UpdateCalendarSyncSettingsDto } from '../../calendar-sync/dto/update-calendar-sync-settings.dto';
import { UpdateBottlingBatchDto } from '../../bottling/dto/update-bottling-batch.dto';
import { UpdateColonyDto } from '../../colonies/dto/update-colony.dto';
import { UpdateDocumentDto } from '../../documents/dto/update-document.dto';
import { UpdateEquipmentDto } from '../../equipment/dto/update-equipment.dto';
import { UpdateExtractionDto } from '../../extractions/dto/update-extraction.dto';
import { UpdateFeedingDto } from '../../feedings/dto/update-feeding.dto';
import { UpdateHarvestDto } from '../../harvests/dto/update-harvest.dto';
import { UpdateHoneyBatchDto } from '../../honey-batches/dto/update-honey-batch.dto';
import { UpdateHoneyMeasurementDto } from '../../honey-batches/dto/update-honey-measurement.dto';
import { UpdateInspectionDto } from '../../inspections/dto/update-inspection.dto';
import { UpdateLabelTemplateDto } from '../../label-templates/dto/update-label-template.dto';
import { UpdateNotificationSettingsDto } from '../../notifications/dto/update-notification-settings.dto';
import { UpdatePhenologyReportDto } from '../../phenology/dto/update-phenology-report.dto';
import { UpdateProductionBatchDto } from '../../production-batches/dto/update-production-batch.dto';
import { UpdateProductDto } from '../../products/dto/update-product.dto';
import { UpdateProfileDto } from '../../profile/dto/update-profile.dto';
import { UpdateQueenEvaluationDto } from '../../queens/dto/update-queen-evaluation.dto';
import { UpdateQueenDto } from '../../queens/dto/update-queen.dto';
import { UpdateRawMaterialLotDto } from '../../raw-materials/dto/update-raw-material-lot.dto';
import { UpdateRoleDto } from '../../roles/dto/update-role.dto';
import { UpdateSiteSettingsDto } from '../../site-settings/dto/update-site-settings.dto';
import { UpdateStockItemDto } from '../../storage/dto/update-stock-item.dto';
import { UpdateStorageContainerDto } from '../../storage/dto/update-storage-container.dto';
import { UpdateSwarmControlCheckDto } from '../../swarm-control/dto/update-swarm-control-check.dto';
import { UpdateTaskTemplateDto } from '../../task-templates/dto/update-task-template.dto';
import { UpdateTaskDto } from '../../tasks/dto/update-task.dto';
import { UpdateTreatmentDto } from '../../treatments/dto/update-treatment.dto';
import { UpdateUserDto } from '../../users/dto/update-user.dto';
import { UpdateVarroaMeasurementDto } from '../../varroa/dto/update-varroa-measurement.dto';
import { UpdateWebhookDto } from '../../webhooks/dto/update-webhook.dto';
import { UpdateWinterCheckDto } from '../../winter-checks/dto/update-winter-check.dto';
import {
describeNullContracts,
NullContract,
} from './null-contract.spec-helper';
const contracts: Record<string, NullContract> = {
UpdateApiaryDto: {
dto: UpdateApiaryDto,
notNull: ['name'],
clearable: [
'description',
'location',
'latitude',
'longitude',
'address',
'notes',
'publicLocationLabel',
],
},
UpdateAutomationRuleDto: {
dto: UpdateAutomationRuleDto,
notNull: ['active'],
clearable: ['config', 'actionTemplate'],
},
UpdateBackupSettingsDto: {
dto: UpdateBackupSettingsDto,
notNull: [],
clearable: ['storagePath', 'cronSchedule', 'retentionCount'],
},
// groupId: null clears it, the same as the empty string.
UpdateCalendarSyncSettingsDto: {
dto: UpdateCalendarSyncSettingsDto,
notNull: [
'enabled',
'syncTask',
'syncInspection',
'syncTreatment',
'syncFeeding',
'syncHarvest',
'syncQueenAssignment',
'syncWithdrawal',
],
clearable: ['groupId'],
},
// quietFrom/quietUntil: null clears them, but only as a pair (the service
// judges the merged values).
UpdateNotificationSettingsDto: {
dto: UpdateNotificationSettingsDto,
notNull: [
'taskDueEnabled',
'digestEnabled',
'digestTime',
'telegramCategories',
'telegramDetail',
],
clearable: ['quietFrom', 'quietUntil'],
},
UpdateBottlingBatchDto: {
dto: UpdateBottlingBatchDto,
notNull: [
'honeyBatchId',
'date',
'jarSize',
'jarCount',
'bestBeforeDate',
'lotNumber',
],
clearable: ['productId'],
},
UpdateColonyDto: {
dto: UpdateColonyDto,
notNull: ['apiaryId', 'code', 'colonyType', 'status'],
clearable: [
'hiveType',
'originText',
'strength',
'occupiedFrames',
'broodStatus',
'feedStatus',
'healthStatus',
'notes',
],
},
UpdateDocumentDto: {
dto: UpdateDocumentDto,
notNull: ['entityType', 'entityId', 'fileName'],
clearable: [],
},
UpdateEquipmentDto: {
dto: UpdateEquipmentDto,
notNull: ['inventoryNumber', 'name', 'condition'],
clearable: ['manufacturer', 'purchaseDate', 'location', 'maintenanceDate'],
},
UpdateExtractionDto: {
dto: UpdateExtractionDto,
notNull: ['date', 'harvestIds'],
clearable: ['amount', 'room', 'waterContent', 'temperature', 'notes'],
},
UpdateFeedingDto: {
dto: UpdateFeedingDto,
notNull: ['colonyId', 'date', 'feedType', 'amount', 'unit'],
clearable: ['product', 'batchNumber', 'notes'],
},
UpdateHarvestDto: {
dto: UpdateHarvestDto,
notNull: ['apiaryId', 'date', 'colonyIds'],
clearable: [
'honeyType',
'honeySuperCount',
'grossWeight',
'netWeight',
'notes',
],
},
UpdateHoneyBatchDto: {
dto: UpdateHoneyBatchDto,
notNull: ['batchNumber', 'extractionId', 'status'],
clearable: ['honeyType', 'amount', 'waterContent', 'storageContainerId'],
},
UpdateHoneyMeasurementDto: {
dto: UpdateHoneyMeasurementDto,
notNull: ['waterContent', 'measuredAt'],
clearable: ['device', 'temperature', 'notes'],
},
UpdateInspectionDto: {
dto: UpdateInspectionDto,
notNull: ['colonyId', 'date'],
clearable: [
'weather',
'temperature',
'colonyStrength',
'broodFrames',
'broodAmount',
'broodPattern',
'openBroodPresent',
'cappedBroodPresent',
'eggsPresent',
'queenSeen',
'queenCellsPresent',
'swarmMood',
'gentleness',
'combBehavior',
'feedStatus',
'honeySuperStatus',
'healthIssues',
'varroaIssues',
'measures',
'notes',
],
},
UpdateLabelTemplateDto: {
dto: UpdateLabelTemplateDto,
notNull: [
'name',
'elements',
'widthMm',
'heightMm',
'marginTopMm',
'marginLeftMm',
'gapXMm',
'gapYMm',
'cropMarks',
'isDefault',
],
clearable: ['columns', 'rows'],
},
UpdateProductDto: {
dto: UpdateProductDto,
notNull: ['name', 'status', 'kind', 'shopCategory'],
clearable: [
'description',
'sku',
'packagingSize',
'shelfLifeMonths',
'weight',
'salesUnit',
],
},
// apiaryId: null widens the report to all apiaries; notes: null clears them.
UpdatePhenologyReportDto: {
dto: UpdatePhenologyReportDto,
notNull: ['phase', 'date'],
clearable: ['apiaryId', 'notes'],
},
UpdateProductionBatchDto: {
dto: UpdateProductionBatchDto,
notNull: ['lotNumber', 'date', 'quantity'],
clearable: ['bestBeforeDate', 'notes'],
},
UpdateProfileDto: {
dto: UpdateProfileDto,
notNull: ['name', 'email'],
clearable: [],
},
UpdateQueenDto: {
dto: UpdateQueenDto,
notNull: ['code', 'status'],
clearable: [
'hatchYear',
'breed',
'origin',
'breeder',
'markingColor',
'markingNumber',
'matingType',
'matingStatus',
'inseminationData',
'motherLine',
'fatherLine',
'motherQueenId',
'notes',
],
},
UpdateQueenEvaluationDto: {
dto: UpdateQueenEvaluationDto,
notNull: [
'date',
'colonyStrength',
'gentleness',
'combBuild',
'swarmingTendency',
'broodPattern',
'honeyYield',
],
clearable: ['colonyId', 'notes'],
},
UpdateRawMaterialLotDto: {
dto: UpdateRawMaterialLotDto,
notNull: ['lotNumber', 'date', 'quantity'],
clearable: ['apiaryId', 'harvestId', 'notes'],
},
UpdateRoleDto: {
dto: UpdateRoleDto,
notNull: ['key', 'name', 'permissionKeys'],
clearable: ['description'],
},
// The link fields clear with "" (see the DTO), not null; originCountry is
// NOT NULL with a default.
UpdateSiteSettingsDto: {
dto: UpdateSiteSettingsDto,
notNull: ['originCountry'],
clearable: [],
},
UpdateStockItemDto: {
dto: UpdateStockItemDto,
notNull: ['name', 'category', 'unit', 'quantity'],
clearable: ['minThreshold', 'notes'],
},
UpdateStorageContainerDto: {
dto: UpdateStorageContainerDto,
notNull: ['code'],
clearable: ['containerType', 'capacity', 'location'],
},
UpdateSwarmControlCheckDto: {
dto: UpdateSwarmControlCheckDto,
notNull: [
'colonyId',
'date',
'queenCellsCapped',
'swarmMood',
'actionTaken',
],
clearable: ['queenCellsFound', 'notes', 'nextCheckDate'],
},
UpdateTaskDto: {
dto: UpdateTaskDto,
notNull: ['title', 'priority', 'status', 'relatedType'],
clearable: ['description', 'dueDate', 'assignedUserId', 'relatedId'],
},
UpdateTaskTemplateDto: {
dto: UpdateTaskTemplateDto,
notNull: ['title', 'priority', 'recurrenceType', 'active'],
clearable: [
'description',
'assignedUserId',
'recurrenceMonth',
'recurrenceDayOfMonth',
'intervalDays',
],
},
UpdateTreatmentDto: {
dto: UpdateTreatmentDto,
notNull: ['colonyId', 'date', 'product'],
clearable: [
'startDate',
'endDate',
'activeIngredient',
'amount',
'method',
'reason',
'batchNumber',
'supplier',
'purchaseProof',
'prescribingVet',
'notes',
'withdrawalPeriodDays',
],
},
UpdateUserDto: {
dto: UpdateUserDto,
notNull: ['name', 'email', 'roleId'],
clearable: [],
},
UpdateVarroaMeasurementDto: {
dto: UpdateVarroaMeasurementDto,
notNull: ['colonyId', 'date', 'method'],
clearable: [
'durationMinutes',
'miteCount',
'sampleBees',
'calculatedValue',
'assessment',
'notes',
],
},
UpdateWebhookDto: {
dto: UpdateWebhookDto,
notNull: ['url', 'events', 'active'],
clearable: [],
},
UpdateWinterCheckDto: {
dto: UpdateWinterCheckDto,
notNull: ['colonyId', 'season', 'date'],
clearable: [
'occupiedSeams',
'feedStockKg',
'queenPresent',
'outcome',
'lossCause',
'notes',
],
},
};
describeNullContracts('update DTO null handling', contracts);