Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions apps/api/drizzle/0044_supply_line_supply_id.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
-- Soft link de cada línea de material al catálogo maestro (#223): columna
-- `supply_id` opcional (FK a supplies(id) ON DELETE SET NULL) en las 4 tablas
-- de líneas. `name` se conserva (legacy + "Otro"). Las líneas en jsonb
-- (containers.lines, shipments.items) heredan el campo vía SupplyLineSnapshot,
-- sin columna.
--
-- Idempotente: ADD COLUMN IF NOT EXISTS + DROP CONSTRAINT IF EXISTS antes de
-- ADD CONSTRAINT (patrón del repo, p.ej. 0011), para reaplicar sin error.

ALTER TABLE "need_items" ADD COLUMN IF NOT EXISTS "supply_id" uuid;
--> statement-breakpoint
ALTER TABLE "need_items" DROP CONSTRAINT IF EXISTS "need_items_supply_id_fkey";
--> statement-breakpoint
ALTER TABLE "need_items" ADD CONSTRAINT "need_items_supply_id_fkey"
FOREIGN KEY ("supply_id") REFERENCES "supplies"("id") ON DELETE SET NULL;
--> statement-breakpoint

ALTER TABLE "offer_items" ADD COLUMN IF NOT EXISTS "supply_id" uuid;
--> statement-breakpoint
ALTER TABLE "offer_items" DROP CONSTRAINT IF EXISTS "offer_items_supply_id_fkey";
--> statement-breakpoint
ALTER TABLE "offer_items" ADD CONSTRAINT "offer_items_supply_id_fkey"
FOREIGN KEY ("supply_id") REFERENCES "supplies"("id") ON DELETE SET NULL;
--> statement-breakpoint

ALTER TABLE "resource_items" ADD COLUMN IF NOT EXISTS "supply_id" uuid;
--> statement-breakpoint
ALTER TABLE "resource_items" DROP CONSTRAINT IF EXISTS "resource_items_supply_id_fkey";
--> statement-breakpoint
ALTER TABLE "resource_items" ADD CONSTRAINT "resource_items_supply_id_fkey"
FOREIGN KEY ("supply_id") REFERENCES "supplies"("id") ON DELETE SET NULL;
--> statement-breakpoint

ALTER TABLE "donation_intake_lines" ADD COLUMN IF NOT EXISTS "supply_id" uuid;
--> statement-breakpoint
ALTER TABLE "donation_intake_lines" DROP CONSTRAINT IF EXISTS "donation_intake_lines_supply_id_fkey";
--> statement-breakpoint
ALTER TABLE "donation_intake_lines" ADD CONSTRAINT "donation_intake_lines_supply_id_fkey"
FOREIGN KEY ("supply_id") REFERENCES "supplies"("id") ON DELETE SET NULL;
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface ShipmentItemView {
unit: string | null;
category: string;
presentation: string | null;
supplyId: string | null;
}

export interface ShipmentView {
Expand All @@ -32,6 +33,7 @@ function toItemView(i: SupplyLineSnapshot): ShipmentItemView {
unit: i.unit,
category: i.category,
presentation: i.presentation ?? null,
supplyId: i.supplyId ?? null,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ export class ShipmentController {
unit: i.unit ?? null,
category: i.category,
presentation: i.presentation ?? null,
supplyId: i.supplyId ?? null,
})),
containerIds: dto.containerIds ?? [],
manifest: dto.manifest ?? null,
Expand Down
3 changes: 3 additions & 0 deletions apps/api/src/contexts/needs/application/create-need.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export interface CreateNeedItemCommand {
/** Presentation / route of administration (#61). Optional. */
presentation?: string | null;
expiresAt?: string | null;
/** Soft link to the master catalogue supply (#223). Optional. */
supplyId?: string | null;
}

export interface CreateNeedLocationCommand {
Expand Down Expand Up @@ -81,6 +83,7 @@ export class CreateNeed {
category: i.category,
presentation: i.presentation ?? null,
expiresAt: i.expiresAt ?? null,
supplyId: i.supplyId ?? null,
}),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export class NeedsController {
category: i.category,
presentation: i.presentation ?? null,
expiresAt: i.expiresAt ?? null,
supplyId: i.supplyId ?? null,
})),
requiredSkill: dto.requiredSkill ?? null,
skillSpecialty: dto.skillSpecialty ?? null,
Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/contexts/offers/application/edit-offer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface EditOfferItemCommand {
unit: string | null;
category: Category;
presentation: string | null;
supplyId?: string | null;
}

export interface EditOfferCommand {
Expand Down Expand Up @@ -60,6 +61,7 @@ export class EditOffer {
unit: i.unit,
category: i.category,
presentation: i.presentation,
supplyId: i.supplyId ?? null,
}),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface DonationIntakeLineView {
category: Category;
presentation: string | null;
expiresAt: string | null;
supplyId: string | null;
}

export interface DonationIntakeView {
Expand Down Expand Up @@ -62,6 +63,7 @@ export class GetDonationIntakeById {
category: line.category,
presentation: line.presentation ?? null,
expiresAt: line.expiresAt ?? null,
supplyId: line.supplyId ?? null,
})),
volunteerNotes: snap.volunteerNotes,
evidenceFileKey: snap.evidenceFileKey,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ describe('GetDonationIntakeTracking', () => {
unit: 'l',
category: Category.Water,
presentation: null,
supplyId: null,
},
]);
// No third-party PII leaks into the public view.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface DonationIntakeTrackingLine {
unit: string | null;
category: Category;
presentation: string | null;
supplyId: string | null;
}

export interface DonationIntakeTracking {
Expand Down Expand Up @@ -64,6 +65,7 @@ export class GetDonationIntakeTracking {
unit: line.unit,
category: line.category,
presentation: line.presentation ?? null,
supplyId: line.supplyId ?? null,
})),
};
}
Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/contexts/offers/application/submit-offer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface SubmitOfferItemCommand {
unit: string | null;
category: Category;
presentation: string | null;
supplyId?: string | null;
}

export interface SubmitOfferCommand {
Expand Down Expand Up @@ -100,6 +101,7 @@ export class SubmitOffer {
unit: i.unit,
category: i.category,
presentation: i.presentation,
supplyId: i.supplyId ?? null,
}),
);

Expand Down
1 change: 1 addition & 0 deletions apps/api/src/contexts/offers/domain/intake-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export class IntakeLine {
category: s.category,
presentation: s.presentation ?? null,
expiresAt: s.expiresAt ?? null,
supplyId: s.supplyId ?? null,
}),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ function mapItems(items: CreateDonationIntakeDto['items']): SupplyLineProps[] {
category: item.category,
presentation: item.presentation ?? null,
expiresAt: item.expiresAt ?? null,
supplyId: item.supplyId ?? null,
}));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ export class OffersController {
unit: i.unit ?? null,
category: i.category,
presentation: i.presentation ?? null,
supplyId: i.supplyId ?? null,
})),
location: {
address: dto.location.address,
Expand Down Expand Up @@ -337,6 +338,7 @@ export class OffersController {
unit: i.unit ?? null,
category: i.category,
presentation: i.presentation ?? null,
supplyId: i.supplyId ?? null,
}));
}
if (dto.notes !== undefined) cmd.notes = dto.notes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface RegisterResourceCommand {
category: Category;
presentation?: string | null;
expiresAt?: string | null;
supplyId?: string | null;
}>;
/** Optional restricted author attribution (#235). */
author?: AuthorProps | null;
Expand Down Expand Up @@ -89,6 +90,7 @@ export class RegisterResource {
category: i.category,
presentation: i.presentation ?? null,
expiresAt: i.expiresAt ?? null,
supplyId: i.supplyId ?? null,
}),
),
author: cmd.author ? Author.create(cmd.author) : null,
Expand Down
56 changes: 56 additions & 0 deletions apps/api/src/contexts/resources/domain/resource.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,65 @@ describe('Resource', () => {
category: Category.Water,
presentation: null,
expiresAt: null,
supplyId: null,
});
});

it('carries the catalogue soft link (supplyId) through register → snapshot', () => {
const supplyId = '22222222-2222-4222-8222-222222222222';
const r = Resource.register({
id: ResourceId.create(),
emergencyId: EmergencyId.fromString(
'11111111-1111-4111-8111-111111111111',
),
type: ResourceType.Warehouse,
stage: ResourceStage.Origin,
name: 'Almacén catalogado',
location: makeLocation(),
ownerUserId: 'user-abc-123',
items: [
SupplyLine.create({
name: 'Agua',
quantity: 100,
unit: 'litros',
category: Category.Water,
supplyId,
}),
],
});

const restored = Resource.fromSnapshot(r.toSnapshot());
expect(restored.items[0].supplyId).toBe(supplyId);
});

it('does not merge a cataloged line with a free-text line of the same name', () => {
const supplyId = '22222222-2222-4222-8222-222222222222';
const r = make();
r.receiveInventory([
SupplyLine.create({
name: 'Agua',
quantity: 5,
unit: 'l',
category: Category.Water,
supplyId,
}),
SupplyLine.create({
name: 'Agua',
quantity: 3,
unit: 'l',
category: Category.Water,
}),
]);

expect(r.items).toHaveLength(2);
expect(
r.items.map((i) => ({ supplyId: i.supplyId, quantity: i.quantity })),
).toEqual([
{ supplyId, quantity: 5 },
{ supplyId: null, quantity: 3 },
]);
});

it('stores stage, location, ownerUserId and optional ownerOrganizationId', () => {
const r = Resource.register({
id: ResourceId.create(),
Expand Down
3 changes: 2 additions & 1 deletion apps/api/src/contexts/resources/domain/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ export class Resource {
receiveInventory(lines: SupplyLine[]): void {
if (lines.length === 0) return;
const keyOf = (l: SupplyLine): string =>
JSON.stringify([l.name, l.category, l.unit, l.presentation]);
JSON.stringify([l.name, l.category, l.unit, l.presentation, l.supplyId]);
const byKey = new Map<string, SupplyLine>();
for (const item of this._items) byKey.set(keyOf(item), item);
for (const incoming of lines) {
Expand All @@ -343,6 +343,7 @@ export class Resource {
unit: incoming.unit,
category: incoming.category,
presentation: incoming.presentation,
supplyId: incoming.supplyId,
}),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ describe('DrizzleResourceRepository (integration)', () => {
category: Category.Water,
presentation: null,
expiresAt: '2026-07-01',
supplyId: null,
},
{
name: 'Mantas',
Expand All @@ -113,6 +114,7 @@ describe('DrizzleResourceRepository (integration)', () => {
category: Category.Shelter,
presentation: null,
expiresAt: null,
supplyId: null,
},
]),
);
Expand Down Expand Up @@ -143,6 +145,7 @@ describe('DrizzleResourceRepository (integration)', () => {
category: Category.Food,
presentation: null,
expiresAt: null,
supplyId: null,
},
]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export class ResourcesController {
category: i.category,
presentation: i.presentation ?? null,
expiresAt: i.expiresAt ?? null,
supplyId: i.supplyId ?? null,
})),
author: dto.author ?? null,
});
Expand Down Expand Up @@ -187,6 +188,7 @@ export class ResourcesController {
unit: i.unit ?? null,
category: i.category,
presentation: i.presentation ?? null,
supplyId: i.supplyId ?? null,
})),
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface ContainerLineView {
unit: string | null;
category: string;
presentation: string | null;
supplyId: string | null;
}

export interface ContainerView {
Expand Down Expand Up @@ -43,6 +44,7 @@ function toLineView(l: SupplyLineSnapshot): ContainerLineView {
unit: l.unit,
category: l.category,
presentation: l.presentation ?? null,
supplyId: l.supplyId ?? null,
};
}

Expand Down
Loading
Loading