feat(suppliers)!: full customers model - structured address + custom fields
The dodavatele modal is now an exact mirror of the customers modal (minus the PDF field-order picker): Nazev+ARES, Ulice, Mesto+PSC, Zeme, ICO+ARES, DIC, and the same "Vlastni pole" editor (maxWidth md). Two migrations on sklad_suppliers: - structured street/city/postal_code/country replace the free-text address blob (best-effort split: street / PSC regex / city) - custom_fields LONGTEXT replaces contact_person/email/phone/notes; existing values are preserved as labeled custom fields The encode/decode of the custom_fields blob is shared with customers via the new utils/custom-fields.ts. The PO PDF supplier block renders the structured address + custom-field lines like the customer block; the supplier picker/detail selects and types follow. Legacy clients sending the dropped keys are silently stripped (tested). Suppliers list shows Ulice/Mesto instead of the dropped contact columns; search covers name/ico/city. BREAKING CHANGE: sklad_suppliers.address, contact_person, email, phone, notes columns dropped (data migrated); deploy must run prisma migrate deploy + generate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,10 @@ import { logAudit } from "../../services/audit";
|
||||
import { success, error, parseId, paginated } from "../../utils/response";
|
||||
import { contentDisposition } from "../../utils/content-disposition";
|
||||
import { parsePagination, buildPaginationMeta } from "../../utils/pagination";
|
||||
import {
|
||||
encodeCustomFields,
|
||||
decodeCustomFields,
|
||||
} from "../../utils/custom-fields";
|
||||
import { parseBody } from "../../schemas/common";
|
||||
import { nasInvoicesManager } from "../../services/nas-financials-manager";
|
||||
import {
|
||||
@@ -226,7 +230,7 @@ export default async function warehouseRoutes(
|
||||
// SUPPLIERS
|
||||
// =============================================================
|
||||
|
||||
// GET /suppliers — paginated list, search by name/ico/contact_person
|
||||
// GET /suppliers — paginated list, search by name/ico/city
|
||||
fastify.get(
|
||||
"/suppliers",
|
||||
{ preHandler: requirePermission("warehouse.manage") },
|
||||
@@ -240,7 +244,7 @@ export default async function warehouseRoutes(
|
||||
where.OR = [
|
||||
{ name: { contains: search } },
|
||||
{ ico: { contains: search } },
|
||||
{ contact_person: { contains: search } },
|
||||
{ city: { contains: search } },
|
||||
];
|
||||
}
|
||||
|
||||
@@ -254,9 +258,16 @@ export default async function warehouseRoutes(
|
||||
prisma.sklad_suppliers.count({ where }),
|
||||
]);
|
||||
|
||||
// Decode the custom_fields blob into the array shape the modal edits
|
||||
// (same model as customers).
|
||||
const enriched = suppliers.map((s) => ({
|
||||
...s,
|
||||
custom_fields: decodeCustomFields(s.custom_fields).custom_fields,
|
||||
}));
|
||||
|
||||
return paginated(
|
||||
reply,
|
||||
suppliers,
|
||||
enriched,
|
||||
buildPaginationMeta(total, page, limit),
|
||||
);
|
||||
},
|
||||
@@ -275,7 +286,10 @@ export default async function warehouseRoutes(
|
||||
});
|
||||
if (!supplier) return error(reply, "Dodavatel nenalezen", 404);
|
||||
|
||||
return success(reply, supplier);
|
||||
return success(reply, {
|
||||
...supplier,
|
||||
custom_fields: decodeCustomFields(supplier.custom_fields).custom_fields,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -293,11 +307,12 @@ export default async function warehouseRoutes(
|
||||
name: body.name,
|
||||
ico: body.ico ?? null,
|
||||
dic: body.dic ?? null,
|
||||
contact_person: body.contact_person ?? null,
|
||||
email: body.email ?? null,
|
||||
phone: body.phone ?? null,
|
||||
address: body.address ?? null,
|
||||
notes: body.notes ?? null,
|
||||
street: body.street ?? null,
|
||||
city: body.city ?? null,
|
||||
postal_code: body.postal_code ?? null,
|
||||
country: body.country ?? null,
|
||||
// Suppliers have no PDF field-order picker — order is always [].
|
||||
custom_fields: encodeCustomFields(body.custom_fields, []),
|
||||
is_active: body.is_active ?? true,
|
||||
},
|
||||
});
|
||||
@@ -312,7 +327,7 @@ export default async function warehouseRoutes(
|
||||
newValues: {
|
||||
name: supplier.name,
|
||||
ico: supplier.ico,
|
||||
contact_person: supplier.contact_person,
|
||||
city: supplier.city,
|
||||
is_active: supplier.is_active,
|
||||
},
|
||||
});
|
||||
@@ -342,12 +357,13 @@ export default async function warehouseRoutes(
|
||||
if (body.name !== undefined) updateData.name = body.name;
|
||||
if (body.ico !== undefined) updateData.ico = body.ico;
|
||||
if (body.dic !== undefined) updateData.dic = body.dic;
|
||||
if (body.contact_person !== undefined)
|
||||
updateData.contact_person = body.contact_person;
|
||||
if (body.email !== undefined) updateData.email = body.email;
|
||||
if (body.phone !== undefined) updateData.phone = body.phone;
|
||||
if (body.address !== undefined) updateData.address = body.address;
|
||||
if (body.notes !== undefined) updateData.notes = body.notes;
|
||||
if (body.street !== undefined) updateData.street = body.street;
|
||||
if (body.city !== undefined) updateData.city = body.city;
|
||||
if (body.postal_code !== undefined)
|
||||
updateData.postal_code = body.postal_code;
|
||||
if (body.country !== undefined) updateData.country = body.country;
|
||||
if (body.custom_fields !== undefined)
|
||||
updateData.custom_fields = encodeCustomFields(body.custom_fields, []);
|
||||
if (body.is_active !== undefined) updateData.is_active = body.is_active;
|
||||
|
||||
const updated = await prisma.sklad_suppliers.update({
|
||||
@@ -365,13 +381,13 @@ export default async function warehouseRoutes(
|
||||
oldValues: {
|
||||
name: existing.name,
|
||||
ico: existing.ico,
|
||||
contact_person: existing.contact_person,
|
||||
city: existing.city,
|
||||
is_active: existing.is_active,
|
||||
},
|
||||
newValues: {
|
||||
name: updated.name,
|
||||
ico: updated.ico,
|
||||
contact_person: updated.contact_person,
|
||||
city: updated.city,
|
||||
is_active: updated.is_active,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user