refactor: rename remaining recipient references to person/people

This commit is contained in:
2026-06-16 13:34:15 +02:00
parent 29c7c19cd8
commit cf6820a7aa
33 changed files with 225 additions and 213 deletions
+2 -2
View File
@@ -134,7 +134,7 @@ model Assignment {
assetId String? @unique assetId String? @unique
asset Asset? @relation(fields: [assetId], references: [id], onDelete: SetNull, onUpdate: Cascade) asset Asset? @relation(fields: [assetId], references: [id], onDelete: SetNull, onUpdate: Cascade)
recipientId String? recipientId String?
recipient Person? @relation(fields: [recipientId], references: [id], onDelete: Cascade, onUpdate: Cascade) person Person? @relation(fields: [recipientId], references: [id], onDelete: Cascade, onUpdate: Cascade)
assignmentDate DateTime @default(now()) assignmentDate DateTime @default(now())
returnDate DateTime? returnDate DateTime?
createdBy String createdBy String
@@ -171,7 +171,7 @@ model Movement {
previousStock Int? previousStock Int?
newStock Int? newStock Int?
recipientId String? recipientId String?
recipient Person? @relation(fields: [recipientId], references: [id], onDelete: SetNull, onUpdate: Cascade) person Person? @relation(fields: [recipientId], references: [id], onDelete: SetNull, onUpdate: Cascade)
assignmentId String? assignmentId String?
assignment Assignment? @relation(fields: [assignmentId], references: [id], onDelete: SetNull, onUpdate: Cascade) assignment Assignment? @relation(fields: [assignmentId], references: [id], onDelete: SetNull, onUpdate: Cascade)
userId String userId String
+3 -3
View File
@@ -308,7 +308,7 @@ export async function importItems(formData: ImportFormType) {
notes: deliveryNote || "", notes: deliveryNote || "",
itemId: newItem?.id || "", itemId: newItem?.id || "",
assetId: newAsset?.id || "", assetId: newAsset?.id || "",
recipientId: newPerson?.id || "", personId: newPerson?.id || "",
assignmentDate: new Date(), assignmentDate: new Date(),
createdBy: userId, createdBy: userId,
}) })
@@ -319,7 +319,7 @@ export async function importItems(formData: ImportFormType) {
quantity: stock || 1, quantity: stock || 1,
type: assigned ? "ASSIGNMENT" : "IN", type: assigned ? "ASSIGNMENT" : "IN",
itemId: newItem?.id || undefined, itemId: newItem?.id || undefined,
recipientId: newPerson?.id || undefined, personId: newPerson?.id || undefined,
} }
if (newAssignment?.id) { if (newAssignment?.id) {
@@ -327,7 +327,7 @@ export async function importItems(formData: ImportFormType) {
} }
if (newPerson?.id) { if (newPerson?.id) {
movementData.recipientId = newPerson.id movementData.personId = newPerson.id
} }
await MovementService.create({ await MovementService.create({
+1 -1
View File
@@ -77,7 +77,7 @@ export default async function Home() {
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke="currentColor"
role="img" role="img"
aria-label="total-recipients" aria-label="total-people"
> >
<path <path
strokeLinecap="round" strokeLinecap="round"
@@ -1,5 +1,4 @@
import { getI18n } from "@/i18n/server" import { getI18n } from "@/i18n/server"
import type { UpdateAssignmentFormType } from "@/schemas/assignment.schema"
import { AssetService } from "@/services/asset.service" import { AssetService } from "@/services/asset.service"
import { AssignmentService } from "@/services/assignment.service" import { AssignmentService } from "@/services/assignment.service"
import { ItemService } from "@/services/item.service" import { ItemService } from "@/services/item.service"
@@ -37,10 +36,19 @@ export default async function EditAssignmentPage({
<h1 className="text-2xl font-bold">{copy.edit.title}</h1> <h1 className="text-2xl font-bold">{copy.edit.title}</h1>
</div> </div>
<AssignmentForm <AssignmentForm
recipients={people} people={people}
items={items} items={items}
assets={assets} assets={assets}
initialData={assignment as UpdateAssignmentFormType} initialData={{
...assignment,
id: assignment.id,
personId: assignment.recipientId ?? "",
itemId: assignment.itemId ?? undefined,
assetId: assignment.assetId ?? undefined,
quantity: assignment.quantity ?? undefined,
notes: assignment.notes ?? undefined,
assignmentDate: assignment.assignmentDate ?? undefined,
}}
formCopy={copy.form} formCopy={copy.form}
schemaCopy={copy.schema} schemaCopy={copy.schema}
submitButtonCopy={dictionary.common.submitButton} submitButtonCopy={dictionary.common.submitButton}
@@ -21,7 +21,7 @@ type AssignmentFormCopy = Dictionary["inventory"]["assignments"]["form"]
type AssignmentSchemaCopy = Dictionary["inventory"]["assignments"]["schema"] type AssignmentSchemaCopy = Dictionary["inventory"]["assignments"]["schema"]
interface Props { interface Props {
recipients: Person[] people: Person[]
items: Item[] items: Item[]
assets: Asset[] assets: Asset[]
initialData: UpdateAssignmentFormType initialData: UpdateAssignmentFormType
@@ -31,7 +31,7 @@ interface Props {
} }
export default function EditAssignmentForm({ export default function EditAssignmentForm({
recipients, people,
items, items,
assets, assets,
initialData, initialData,
@@ -84,24 +84,24 @@ export default function EditAssignmentForm({
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}> <form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
<input type="hidden" {...register("id")} /> <input type="hidden" {...register("id")} />
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<label htmlFor="recipientId" className="mb-2 block text-lg"> <label htmlFor="personId" className="mb-2 block text-lg">
{formCopy.recipientLabel} {formCopy.personLabel}
</label> </label>
<select <select
id="recipientId" id="personId"
{...register("recipientId")} {...register("personId")}
className={`w-full rounded-lg border px-4 py-2 ${ className={`w-full rounded-lg border px-4 py-2 ${
errors.recipientId ? "border-error" : "" errors.personId ? "border-error" : ""
}`} }`}
> >
{recipients.map((recipient) => ( {people.map((person) => (
<option key={recipient.id} value={recipient.id}> <option key={person.id} value={person.id}>
{recipient.firstName} {recipient.lastName} {person.firstName} {person.lastName}
</option> </option>
))} ))}
</select> </select>
{errors.recipientId && ( {errors.personId && (
<p className="text-error">{errors.recipientId.message}</p> <p className="text-error">{errors.personId.message}</p>
)} )}
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
@@ -21,7 +21,7 @@ type AssignmentFormCopy = Dictionary["inventory"]["assignments"]["form"]
type AssignmentSchemaCopy = Dictionary["inventory"]["assignments"]["schema"] type AssignmentSchemaCopy = Dictionary["inventory"]["assignments"]["schema"]
interface Props { interface Props {
recipients: Person[] people: Person[]
items: Item[] items: Item[]
assets: Asset[] assets: Asset[]
formCopy: AssignmentFormCopy formCopy: AssignmentFormCopy
@@ -30,7 +30,7 @@ interface Props {
} }
export default function CreateAssignmentForm({ export default function CreateAssignmentForm({
recipients, people,
items, items,
assets, assets,
formCopy, formCopy,
@@ -81,25 +81,25 @@ export default function CreateAssignmentForm({
return ( return (
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}> <form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<label htmlFor="recipientId" className="mb-2 block text-lg"> <label htmlFor="personId" className="mb-2 block text-lg">
{formCopy.recipientLabel} {formCopy.personLabel}
</label> </label>
<select <select
id="recipientId" id="personId"
{...register("recipientId")} {...register("personId")}
className={`w-full rounded-lg border px-4 py-2 ${ className={`w-full rounded-lg border px-4 py-2 ${
errors.recipientId ? "border-error" : "" errors.personId ? "border-error" : ""
}`} }`}
> >
<option value="">{formCopy.recipientPlaceholder}</option> <option value="">{formCopy.personPlaceholder}</option>
{recipients.map((recipient) => ( {people.map((person) => (
<option key={recipient.id} value={recipient.id}> <option key={person.id} value={person.id}>
{recipient.firstName} {recipient.lastName} {person.firstName} {person.lastName}
</option> </option>
))} ))}
</select> </select>
{errors.recipientId && ( {errors.personId && (
<p className="text-error">{errors.recipientId.message}</p> <p className="text-error">{errors.personId.message}</p>
)} )}
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
+1 -1
View File
@@ -18,7 +18,7 @@ export default async function NewAssignmentPage() {
<h1 className="text-2xl font-bold">{copy.new.title}</h1> <h1 className="text-2xl font-bold">{copy.new.title}</h1>
</div> </div>
<AssignmentForm <AssignmentForm
recipients={people} people={people}
items={items} items={items}
assets={assets} assets={assets}
formCopy={copy.form} formCopy={copy.form}
+5 -5
View File
@@ -19,7 +19,7 @@ export default async function AssignmentsPage(props: {
const currentPage = searchParams?.page ? parseInt(searchParams.page, 10) : 1 const currentPage = searchParams?.page ? parseInt(searchParams.page, 10) : 1
const search = searchParams?.search || "" const search = searchParams?.search || ""
const { data: assignments, totalPages } = const { data: assignments, totalPages } =
await AssignmentService.findAllWithRecipientPaginated({ await AssignmentService.findAllWithPersonPaginated({
page: currentPage, page: currentPage,
search, search,
}) })
@@ -42,7 +42,7 @@ export default async function AssignmentsPage(props: {
<thead className="border-b"> <thead className="border-b">
<tr> <tr>
<th scope="col" className="p-4"> <th scope="col" className="p-4">
{copy.list.columns.recipient} {copy.list.columns.person}
</th> </th>
<th scope="col" className="p-4"> <th scope="col" className="p-4">
{copy.list.columns.item} {copy.list.columns.item}
@@ -63,11 +63,11 @@ export default async function AssignmentsPage(props: {
<tr key={assignment.id} className="border-b"> <tr key={assignment.id} className="border-b">
<td className="p-4"> <td className="p-4">
<Link <Link
href={`/people/${assignment?.recipient?.id}`} href={`/people/${assignment?.person?.id}`}
className="hover:underline" className="hover:underline"
> >
{assignment?.recipient?.firstName}{" "} {assignment?.person?.firstName}{" "}
{assignment?.recipient?.lastName} {assignment?.person?.lastName}
</Link> </Link>
</td> </td>
<td className="p-4"> <td className="p-4">
@@ -31,7 +31,7 @@ export default async function EditAssetPage({
</div> </div>
<EditAssetForm <EditAssetForm
items={items} items={items}
recipients={people} people={people}
asset={asset as unknown as AssetWithAssignment} asset={asset as unknown as AssetWithAssignment}
formCopy={copy.form} formCopy={copy.form}
schemaCopy={copy.schema} schemaCopy={copy.schema}
@@ -31,7 +31,7 @@ import type {
interface EditAssetFormProps { interface EditAssetFormProps {
asset: AssetWithAssignment asset: AssetWithAssignment
items: Item[] items: Item[]
recipients: Person[] people: Person[]
formCopy: AssetFormCopy formCopy: AssetFormCopy
schemaCopy: AssetSchemaCopy schemaCopy: AssetSchemaCopy
statusCopy: AssetStatusCopy statusCopy: AssetStatusCopy
@@ -41,7 +41,7 @@ interface EditAssetFormProps {
export default function EditAssetForm({ export default function EditAssetForm({
asset, asset,
items, items,
recipients, people,
formCopy, formCopy,
schemaCopy, schemaCopy,
statusCopy, statusCopy,
@@ -64,7 +64,7 @@ export default function EditAssetForm({
serialNumber: asset.serialNumber, serialNumber: asset.serialNumber,
deliveryNote: asset.deliveryNote ?? undefined, deliveryNote: asset.deliveryNote ?? undefined,
status: asset.status as UpdateAssetStatus, status: asset.status as UpdateAssetStatus,
recipientId: asset.assignment?.recipientId ?? undefined, personId: asset.assignment?.recipientId ?? undefined,
}, },
shouldFocusError: true, shouldFocusError: true,
mode: "onSubmit", mode: "onSubmit",
@@ -168,23 +168,23 @@ export default function EditAssetForm({
</div> </div>
{status === "ASSIGNED" && ( {status === "ASSIGNED" && (
<div> <div>
<label htmlFor="recipientId" className="mb-2 block text-lg"> <label htmlFor="personId" className="mb-2 block text-lg">
{formCopy.recipientLabel} {formCopy.personLabel}
</label> </label>
<select <select
id="recipientId" id="personId"
{...register("recipientId")} {...register("personId")}
className="w-full rounded-lg border px-4 py-2" className="w-full rounded-lg border px-4 py-2"
> >
<option value="">{formCopy.recipientPlaceholder}</option> <option value="">{formCopy.personPlaceholder}</option>
{recipients?.map((recipient) => ( {people?.map((person) => (
<option key={recipient.id} value={recipient.id}> <option key={person.id} value={person.id}>
{recipient.firstName} {recipient.lastName} {person.firstName} {person.lastName}
</option> </option>
))} ))}
</select> </select>
{errors?.recipientId && ( {errors?.personId && (
<p className="text-error">{errors.recipientId.message}</p> <p className="text-error">{errors.personId.message}</p>
)} )}
</div> </div>
)} )}
@@ -25,7 +25,7 @@ import type {
interface NewAssetFormProps { interface NewAssetFormProps {
items: ItemWithoutStock[] items: ItemWithoutStock[]
recipients: Person[] people: Person[]
formCopy: AssetFormCopy formCopy: AssetFormCopy
schemaCopy: AssetSchemaCopy schemaCopy: AssetSchemaCopy
statusCopy: AssetStatusCopy statusCopy: AssetStatusCopy
@@ -34,7 +34,7 @@ interface NewAssetFormProps {
export default function NewAssetForm({ export default function NewAssetForm({
items, items,
recipients, people,
formCopy, formCopy,
schemaCopy, schemaCopy,
statusCopy, statusCopy,
@@ -156,23 +156,23 @@ export default function NewAssetForm({
</div> </div>
{status === "ASSIGNED" && ( {status === "ASSIGNED" && (
<div> <div>
<label htmlFor="recipientId" className="mb-2 block text-lg"> <label htmlFor="personId" className="mb-2 block text-lg">
{formCopy.recipientLabel} {formCopy.personLabel}
</label> </label>
<select <select
id="recipientId" id="personId"
{...register("recipientId")} {...register("personId")}
className="w-full rounded-lg border px-4 py-2" className="w-full rounded-lg border px-4 py-2"
> >
<option value="">{formCopy.recipientPlaceholder}</option> <option value="">{formCopy.personPlaceholder}</option>
{recipients?.map((recipient) => ( {people?.map((person) => (
<option key={recipient.id} value={recipient.id}> <option key={person.id} value={person.id}>
{recipient.firstName} {recipient.lastName} {person.firstName} {person.lastName}
</option> </option>
))} ))}
</select> </select>
{errors?.recipientId && ( {errors?.personId && (
<p className="text-error">{errors.recipientId.message}</p> <p className="text-error">{errors.personId.message}</p>
)} )}
</div> </div>
)} )}
@@ -19,7 +19,7 @@ export default async function NewAssetPage() {
</div> </div>
<NewAssetForm <NewAssetForm
items={items} items={items}
recipients={people} people={people}
formCopy={copy.form} formCopy={copy.form}
schemaCopy={copy.schema} schemaCopy={copy.schema}
statusCopy={copy.status} statusCopy={copy.status}
+3 -3
View File
@@ -43,7 +43,7 @@ export default async function MovementsPage(props: {
{copy.list.columns.quantity} {copy.list.columns.quantity}
</th> </th>
<th scope="col" className="p-4"> <th scope="col" className="p-4">
{copy.list.columns.recipient} {copy.list.columns.person}
</th> </th>
<th scope="col" className="p-4"> <th scope="col" className="p-4">
{copy.list.columns.date} {copy.list.columns.date}
@@ -69,8 +69,8 @@ export default async function MovementsPage(props: {
</td> </td>
<td className="p-4">{movement.quantity}</td> <td className="p-4">{movement.quantity}</td>
<td className="p-4"> <td className="p-4">
{movement?.recipient {movement?.person
? `${movement.recipient.firstName} ${movement.recipient.lastName}` ? `${movement.person.firstName} ${movement.person.lastName}`
: copy.fallback.missingValue} : copy.fallback.missingValue}
</td> </td>
<td className="p-4">{formatDate(movement.createdAt)}</td> <td className="p-4">{formatDate(movement.createdAt)}</td>
+8 -8
View File
@@ -214,8 +214,8 @@ export const en = {
deliveryNotePlaceholder: "Delivery note", deliveryNotePlaceholder: "Delivery note",
statusLabel: "Status", statusLabel: "Status",
statusPlaceholder: "Select a status", statusPlaceholder: "Select a status",
recipientLabel: "Recipient", personLabel: "Person",
recipientPlaceholder: "Select a recipient", personPlaceholder: "Select a person",
createSubmit: "Create Asset", createSubmit: "Create Asset",
updateSubmit: "Update Asset", updateSubmit: "Update Asset",
}, },
@@ -242,7 +242,7 @@ export const en = {
assignmentAlreadyReturned: "Assignment already returned", assignmentAlreadyReturned: "Assignment already returned",
previousItemNotFound: "Previous item not found for available asset", previousItemNotFound: "Previous item not found for available asset",
insufficientStock: "Item does not have enough stock", insufficientStock: "Item does not have enough stock",
recipientRequired: "Recipient is required", personRequired: "Person is required",
invalidStatus: "Invalid status", invalidStatus: "Invalid status",
genericFailure: "Error processing asset", genericFailure: "Error processing asset",
}, },
@@ -261,7 +261,7 @@ export const en = {
addLabel: "Add Assignment", addLabel: "Add Assignment",
empty: "No assignments found.", empty: "No assignments found.",
columns: { columns: {
recipient: "Recipient", person: "Person",
item: "Item", item: "Item",
serialNumber: "Serial Number", serialNumber: "Serial Number",
quantity: "Quantity", quantity: "Quantity",
@@ -280,8 +280,8 @@ export const en = {
notFound: "Assignment not found", notFound: "Assignment not found",
}, },
form: { form: {
recipientLabel: "Recipient", personLabel: "Person",
recipientPlaceholder: "Select a recipient", personPlaceholder: "Select a person",
itemLabel: "Item", itemLabel: "Item",
itemPlaceholder: "Select an item", itemPlaceholder: "Select an item",
assetLabel: "Asset", assetLabel: "Asset",
@@ -311,7 +311,7 @@ export const en = {
genericFailure: "Error processing assignment", genericFailure: "Error processing assignment",
}, },
schema: { schema: {
recipientRequired: "Recipient is required", personRequired: "Person is required",
itemIdRequired: "Item is required", itemIdRequired: "Item is required",
quantityMinOne: "Quantity must be at least 1", quantityMinOne: "Quantity must be at least 1",
assetIdRequired: "Asset ID is required when item ID is provided", assetIdRequired: "Asset ID is required when item ID is provided",
@@ -402,7 +402,7 @@ export const en = {
item: "Item", item: "Item",
serialNumber: "Serial Number", serialNumber: "Serial Number",
quantity: "Quantity", quantity: "Quantity",
recipient: "Recipient", person: "Person",
date: "Date", date: "Date",
}, },
}, },
+8 -8
View File
@@ -217,8 +217,8 @@ export const es = {
deliveryNotePlaceholder: "Remito", deliveryNotePlaceholder: "Remito",
statusLabel: "Estado", statusLabel: "Estado",
statusPlaceholder: "Selecciona un estado", statusPlaceholder: "Selecciona un estado",
recipientLabel: "Destinatario", personLabel: "Persona",
recipientPlaceholder: "Selecciona un destinatario", personPlaceholder: "Selecciona una persona",
createSubmit: "Crear activo", createSubmit: "Crear activo",
updateSubmit: "Actualizar activo", updateSubmit: "Actualizar activo",
}, },
@@ -246,7 +246,7 @@ export const es = {
previousItemNotFound: previousItemNotFound:
"Artículo anterior no encontrado para el activo disponible", "Artículo anterior no encontrado para el activo disponible",
insufficientStock: "El artículo no tiene stock suficiente", insufficientStock: "El artículo no tiene stock suficiente",
recipientRequired: "El destinatario es obligatorio", personRequired: "La persona es obligatoria",
invalidStatus: "Estado inválido", invalidStatus: "Estado inválido",
genericFailure: "Error al procesar el activo", genericFailure: "Error al procesar el activo",
}, },
@@ -265,7 +265,7 @@ export const es = {
addLabel: "Agregar asignación", addLabel: "Agregar asignación",
empty: "No se encontraron asignaciones.", empty: "No se encontraron asignaciones.",
columns: { columns: {
recipient: "Destinatario", person: "Persona",
item: "Artículo", item: "Artículo",
serialNumber: "Número de serie", serialNumber: "Número de serie",
quantity: "Cantidad", quantity: "Cantidad",
@@ -284,8 +284,8 @@ export const es = {
notFound: "Asignación no encontrada", notFound: "Asignación no encontrada",
}, },
form: { form: {
recipientLabel: "Destinatario", personLabel: "Persona",
recipientPlaceholder: "Selecciona un destinatario", personPlaceholder: "Selecciona una persona",
itemLabel: "Artículo", itemLabel: "Artículo",
itemPlaceholder: "Selecciona un artículo", itemPlaceholder: "Selecciona un artículo",
assetLabel: "Activo", assetLabel: "Activo",
@@ -315,7 +315,7 @@ export const es = {
genericFailure: "Error al procesar la asignación", genericFailure: "Error al procesar la asignación",
}, },
schema: { schema: {
recipientRequired: "El destinatario es obligatorio", personRequired: "La persona es obligatoria",
itemIdRequired: "El artículo es obligatorio", itemIdRequired: "El artículo es obligatorio",
quantityMinOne: "La cantidad debe ser al menos 1", quantityMinOne: "La cantidad debe ser al menos 1",
assetIdRequired: assetIdRequired:
@@ -407,7 +407,7 @@ export const es = {
item: "Artículo", item: "Artículo",
serialNumber: "Número de serie", serialNumber: "Número de serie",
quantity: "Cantidad", quantity: "Cantidad",
recipient: "Destinatario", person: "Persona",
date: "Fecha", date: "Fecha",
}, },
}, },
+1 -1
View File
@@ -35,7 +35,7 @@ function buildAssetBaseSchema(copy: AssetSchemaCopy) {
}), }),
deliveryNote: z.string().optional(), deliveryNote: z.string().optional(),
notes: z.string().optional(), notes: z.string().optional(),
recipientId: z.string().optional(), personId: z.string().optional(),
}) })
} }
+3 -3
View File
@@ -6,7 +6,7 @@ export type AssignmentSchemaCopy =
Dictionary["inventory"]["assignments"]["schema"] Dictionary["inventory"]["assignments"]["schema"]
const defaultAssignmentSchemaCopy: AssignmentSchemaCopy = { const defaultAssignmentSchemaCopy: AssignmentSchemaCopy = {
recipientRequired: "Recipient is required", personRequired: "Person is required",
itemIdRequired: "Item is required", itemIdRequired: "Item is required",
quantityMinOne: "Quantity must be at least 1", quantityMinOne: "Quantity must be at least 1",
assetIdRequired: "Asset ID is required when item ID is provided", assetIdRequired: "Asset ID is required when item ID is provided",
@@ -27,8 +27,8 @@ function buildAssignmentBaseSchema(copy: AssignmentSchemaCopy) {
}) })
.optional(), .optional(),
assetId: z.string().optional(), assetId: z.string().optional(),
recipientId: z.string().min(1, { personId: z.string().min(1, {
error: copy.recipientRequired, error: copy.personRequired,
}), }),
assignmentDate: z.date().optional(), assignmentDate: z.date().optional(),
returnDate: z.date().optional(), returnDate: z.date().optional(),
+1 -1
View File
@@ -11,7 +11,7 @@ export const movementSchema = z.object({
assetId: z.string().optional(), assetId: z.string().optional(),
userId: z.string(), userId: z.string(),
assignmentId: z.string().optional(), assignmentId: z.string().optional(),
recipientId: z.string().optional(), personId: z.string().optional(),
}) })
export const createMovementSchema = movementSchema.omit({ export const createMovementSchema = movementSchema.omit({
+19 -17
View File
@@ -2,12 +2,10 @@ import type { Prisma } from "@/generated/prisma/client"
import { paginate } from "@/lib/paginate" import { paginate } from "@/lib/paginate"
import prisma from "@/lib/prisma" import prisma from "@/lib/prisma"
import type { CreateAssignmentData } from "@/schemas/assignment.schema" import type { CreateAssignmentData } from "@/schemas/assignment.schema"
import type { Assignment, AssignmentWithRecipientItemAsset } from "@/types" import type { Assignment, AssignmentWithPersonItemAsset } from "@/types"
export const AssignmentService = { export const AssignmentService = {
findAllWithRecipient: async (): Promise< findAllWithPerson: async (): Promise<AssignmentWithPersonItemAsset[]> => {
AssignmentWithRecipientItemAsset[]
> => {
return prisma.assignment.findMany({ return prisma.assignment.findMany({
where: { where: {
returnDate: { returnDate: {
@@ -15,7 +13,7 @@ export const AssignmentService = {
}, },
}, },
include: { include: {
recipient: true, person: true,
item: true, item: true,
asset: true, asset: true,
}, },
@@ -24,7 +22,7 @@ export const AssignmentService = {
}, },
}) })
}, },
findAllWithRecipientPaginated: async ({ findAllWithPersonPaginated: async ({
page, page,
pageSize, pageSize,
search, search,
@@ -33,7 +31,7 @@ export const AssignmentService = {
pageSize?: number pageSize?: number
search?: string search?: string
}) => { }) => {
return paginate<AssignmentWithRecipientItemAsset>({ return paginate<AssignmentWithPersonItemAsset>({
model: prisma.assignment, model: prisma.assignment,
page, page,
pageSize, pageSize,
@@ -45,12 +43,12 @@ export const AssignmentService = {
? { ? {
OR: [ OR: [
{ {
recipient: { person: {
firstName: { contains: search, mode: "insensitive" }, firstName: { contains: search, mode: "insensitive" },
}, },
}, },
{ {
recipient: { person: {
lastName: { contains: search, mode: "insensitive" }, lastName: { contains: search, mode: "insensitive" },
}, },
}, },
@@ -59,7 +57,7 @@ export const AssignmentService = {
: {}), : {}),
}, },
include: { include: {
recipient: true, person: true,
item: true, item: true,
asset: true, asset: true,
}, },
@@ -71,23 +69,23 @@ export const AssignmentService = {
findById: async ( findById: async (
id: string, id: string,
db: Prisma.TransactionClient | typeof prisma = prisma, db: Prisma.TransactionClient | typeof prisma = prisma,
): Promise<AssignmentWithRecipientItemAsset | null> => { ): Promise<AssignmentWithPersonItemAsset | null> => {
return db.assignment.findUnique({ return db.assignment.findUnique({
where: { id }, where: { id },
include: { include: {
recipient: true, person: true,
item: true, item: true,
asset: true, asset: true,
}, },
}) })
}, },
findAllByPerson: async ( findAllByPerson: async (
recipientId: string, personId: string,
): Promise<AssignmentWithRecipientItemAsset[]> => { ): Promise<AssignmentWithPersonItemAsset[]> => {
return prisma.assignment.findMany({ return prisma.assignment.findMany({
where: { recipientId }, where: { recipientId: personId },
include: { include: {
recipient: true, person: true,
item: true, item: true,
asset: true, asset: true,
}, },
@@ -97,8 +95,12 @@ export const AssignmentService = {
data: CreateAssignmentData & { createdBy: string }, data: CreateAssignmentData & { createdBy: string },
db: Prisma.TransactionClient | typeof prisma = prisma, db: Prisma.TransactionClient | typeof prisma = prisma,
): Promise<Assignment> => { ): Promise<Assignment> => {
const { personId, ...rest } = data
return db.assignment.create({ return db.assignment.create({
data, data: {
...rest,
recipientId: personId,
},
}) })
}, },
delete: async (id: string): Promise<Assignment> => { delete: async (id: string): Promise<Assignment> => {
+7 -3
View File
@@ -11,7 +11,7 @@ type MovementListResult = {
createdAt: Date createdAt: Date
item: { name: string } | null item: { name: string } | null
asset: { serialNumber: string } | null asset: { serialNumber: string } | null
recipient: { firstName: string; lastName: string } | null person: { firstName: string; lastName: string } | null
} }
export const MovementService = { export const MovementService = {
@@ -38,7 +38,7 @@ export const MovementService = {
serialNumber: true, serialNumber: true,
}, },
}, },
recipient: { person: {
select: { select: {
firstName: true, firstName: true,
lastName: true, lastName: true,
@@ -51,8 +51,12 @@ export const MovementService = {
data: CreateMovementFormType & { userId: string }, data: CreateMovementFormType & { userId: string },
db: Prisma.TransactionClient | typeof prisma = prisma, db: Prisma.TransactionClient | typeof prisma = prisma,
): Promise<Movement> => { ): Promise<Movement> => {
const { personId, ...rest } = data
return await db.movement.create({ return await db.movement.create({
data, data: {
...rest,
recipientId: personId,
},
}) })
}, },
+2 -2
View File
@@ -8,9 +8,9 @@ export type Assignment = PrismaAssignment
export type AssignmentSummary = Pick<Assignment, "id" | "quantity"> export type AssignmentSummary = Pick<Assignment, "id" | "quantity">
export type AssignmentWithRecipientItemAsset = Assignment & { export type AssignmentWithPersonItemAsset = Assignment & {
returnDate: Date | null returnDate: Date | null
recipient: Person | null person: Person | null
item: Item | null item: Item | null
asset: Asset | null asset: Asset | null
} }
+21 -21
View File
@@ -68,7 +68,7 @@ type AssetTransitionInput = {
previousItemId: string | null previousItemId: string | null
nextItemId: string nextItemId: string
activeAssignment: Assignment | null activeAssignment: Assignment | null
nextRecipientId?: string nextPersonId?: string
} }
function getAssetTransition({ function getAssetTransition({
@@ -77,7 +77,7 @@ function getAssetTransition({
previousItemId, previousItemId,
nextItemId, nextItemId,
activeAssignment, activeAssignment,
nextRecipientId, nextPersonId,
}: AssetTransitionInput) { }: AssetTransitionInput) {
return { return {
previousStatus, previousStatus,
@@ -87,13 +87,13 @@ function getAssetTransition({
statusChanged: previousStatus !== nextStatus, statusChanged: previousStatus !== nextStatus,
itemChanged: previousItemId !== nextItemId, itemChanged: previousItemId !== nextItemId,
activeAssignment, activeAssignment,
nextRecipientId, nextPersonId,
hasRecipient: Boolean(nextRecipientId), hasPerson: Boolean(nextPersonId),
wasAssigned: previousStatus === "ASSIGNED", wasAssigned: previousStatus === "ASSIGNED",
willBeAssigned: nextStatus === "ASSIGNED", willBeAssigned: nextStatus === "ASSIGNED",
wasAvailable: previousStatus === "AVAILABLE", wasAvailable: previousStatus === "AVAILABLE",
willBeAvailable: nextStatus === "AVAILABLE", willBeAvailable: nextStatus === "AVAILABLE",
recipientChanged: activeAssignment?.recipientId !== nextRecipientId, personChanged: activeAssignment?.recipientId !== nextPersonId,
} }
} }
@@ -107,7 +107,7 @@ export async function createAssetUseCase(
deliveryNote, deliveryNote,
status, status,
notes, notes,
recipientId, personId,
} = input } = input
try { try {
@@ -141,14 +141,14 @@ export async function createAssetUseCase(
) )
const createdAssignment = const createdAssignment =
status === "ASSIGNED" && recipientId status === "ASSIGNED" && personId
? await AssignmentService.create( ? await AssignmentService.create(
{ {
notes: "", notes: "",
itemId, itemId,
assetId: newAsset.id, assetId: newAsset.id,
quantity: 1, quantity: 1,
recipientId, personId,
assignmentDate: new Date(), assignmentDate: new Date(),
createdBy: actorId, createdBy: actorId,
}, },
@@ -162,7 +162,7 @@ export async function createAssetUseCase(
assetId: newAsset.id, assetId: newAsset.id,
quantity: 1, quantity: 1,
type: status === "ASSIGNED" ? "ASSIGNMENT" : "IN", type: status === "ASSIGNED" ? "ASSIGNMENT" : "IN",
recipientId: createdAssignment?.recipientId || undefined, personId: createdAssignment?.recipientId || undefined,
assignmentId: createdAssignment?.id, assignmentId: createdAssignment?.id,
userId: actorId, userId: actorId,
}, },
@@ -203,7 +203,7 @@ export async function updateAssetUseCase(
deliveryNote, deliveryNote,
status, status,
notes, notes,
recipientId, personId,
} = input } = input
try { try {
@@ -226,7 +226,7 @@ export async function updateAssetUseCase(
previousItemId: currentAsset.itemId, previousItemId: currentAsset.itemId,
nextItemId: itemId, nextItemId: itemId,
activeAssignment: currentAsset.assignment, activeAssignment: currentAsset.assignment,
nextRecipientId: recipientId, nextPersonId: personId,
}) })
const existentAsset = await AssetService.findBySerialNumber( const existentAsset = await AssetService.findBySerialNumber(
@@ -277,7 +277,7 @@ export async function updateAssetUseCase(
? transition.nextItemId ? transition.nextItemId
: activeAssignment.itemId || undefined, : activeAssignment.itemId || undefined,
assetId: activeAssignment.assetId || undefined, assetId: activeAssignment.assetId || undefined,
recipientId: activeAssignment.recipientId || undefined, personId: activeAssignment.recipientId || undefined,
assignmentId: activeAssignment.id, assignmentId: activeAssignment.id,
userId: actorId, userId: actorId,
}, },
@@ -296,7 +296,7 @@ export async function updateAssetUseCase(
if ( if (
transition.statusChanged && transition.statusChanged &&
!transition.hasRecipient && !transition.hasPerson &&
!closedActiveAssignment !closedActiveAssignment
) { ) {
const statusMovementItemId = transition.willBeAssigned const statusMovementItemId = transition.willBeAssigned
@@ -408,18 +408,18 @@ export async function updateAssetUseCase(
} }
} }
if (transition.willBeAssigned && transition.nextRecipientId) { if (transition.willBeAssigned && transition.nextPersonId) {
const activeAssignment = transition.activeAssignment const activeAssignment = transition.activeAssignment
if (activeAssignment) { if (activeAssignment) {
if (transition.recipientChanged) { if (transition.personChanged) {
await MovementService.create( await MovementService.create(
{ {
type: "RETURN", type: "RETURN",
quantity: activeAssignment.quantity || 1, quantity: activeAssignment.quantity || 1,
itemId: activeAssignment.itemId || undefined, itemId: activeAssignment.itemId || undefined,
assetId: activeAssignment.assetId || undefined, assetId: activeAssignment.assetId || undefined,
recipientId: activeAssignment.recipientId || undefined, personId: activeAssignment.recipientId || undefined,
assignmentId: activeAssignment.id, assignmentId: activeAssignment.id,
userId: actorId, userId: actorId,
}, },
@@ -432,7 +432,7 @@ export async function updateAssetUseCase(
quantity: 1, quantity: 1,
itemId, itemId,
assetId: id, assetId: id,
recipientId: transition.nextRecipientId, personId: transition.nextPersonId,
assignmentId: activeAssignment.id, assignmentId: activeAssignment.id,
userId: actorId, userId: actorId,
}, },
@@ -445,7 +445,7 @@ export async function updateAssetUseCase(
createdBy: actorId, createdBy: actorId,
itemId, itemId,
assetId: id, assetId: id,
recipientId: transition.nextRecipientId, recipientId: transition.nextPersonId,
quantity: 1, quantity: 1,
returnDate: null, returnDate: null,
}, },
@@ -457,7 +457,7 @@ export async function updateAssetUseCase(
{ {
itemId, itemId,
assetId: id, assetId: id,
recipientId: transition.nextRecipientId, recipientId: transition.nextPersonId,
quantity: 1, quantity: 1,
returnDate: null, returnDate: null,
}, },
@@ -470,7 +470,7 @@ export async function updateAssetUseCase(
itemId, itemId,
assetId: id, assetId: id,
quantity: 1, quantity: 1,
recipientId: transition.nextRecipientId, personId: transition.nextPersonId,
assignmentDate: new Date(), assignmentDate: new Date(),
createdBy: actorId, createdBy: actorId,
}, },
@@ -483,7 +483,7 @@ export async function updateAssetUseCase(
quantity: 1, quantity: 1,
itemId, itemId,
assetId: id, assetId: id,
recipientId: transition.nextRecipientId, personId: transition.nextPersonId,
assignmentId: createdAssignment.id, assignmentId: createdAssignment.id,
userId: actorId, userId: actorId,
}, },
+10 -10
View File
@@ -81,9 +81,9 @@ function updateAssignmentError(
export async function createAssignmentUseCase( export async function createAssignmentUseCase(
input: CreateAssignmentUseCaseInput, input: CreateAssignmentUseCaseInput,
): Promise<CreateAssignmentUseCaseResult> { ): Promise<CreateAssignmentUseCaseResult> {
const { actorId, itemId, assetId, quantity, recipientId } = input const { actorId, itemId, assetId, quantity, personId } = input
if (!itemId || !recipientId || quantity <= 0) { if (!itemId || !personId || quantity <= 0) {
return createAssignmentError({ error: ["Invalid assignment data"] }) return createAssignmentError({ error: ["Invalid assignment data"] })
} }
@@ -141,7 +141,7 @@ export async function createAssignmentUseCase(
itemId, itemId,
assetId: assetId || undefined, assetId: assetId || undefined,
quantity, quantity,
recipientId, personId,
notes: input.notes, notes: input.notes,
assignmentDate: input.assignmentDate, assignmentDate: input.assignmentDate,
createdBy: actorId, createdBy: actorId,
@@ -155,7 +155,7 @@ export async function createAssignmentUseCase(
assetId: assetId || undefined, assetId: assetId || undefined,
quantity, quantity,
type: "ASSIGNMENT", type: "ASSIGNMENT",
recipientId, personId,
assignmentId: createdAssignment.id, assignmentId: createdAssignment.id,
userId: actorId, userId: actorId,
}, },
@@ -175,7 +175,7 @@ export async function updateAssignmentUseCase(
const { const {
actorId, actorId,
id, id,
recipientId, personId,
itemId, itemId,
assetId, assetId,
quantity, quantity,
@@ -216,14 +216,14 @@ export async function updateAssignmentUseCase(
} }
} }
if (assignment.recipientId !== recipientId) { if (assignment.recipientId !== personId) {
await MovementService.create( await MovementService.create(
{ {
type: "RETURN", type: "RETURN",
quantity: assignment.quantity || 1, quantity: assignment.quantity || 1,
itemId: assignment.itemId || undefined, itemId: assignment.itemId || undefined,
assetId: assignment.assetId || undefined, assetId: assignment.assetId || undefined,
recipientId: assignment.recipientId || undefined, personId: assignment.recipientId || undefined,
assignmentId: id, assignmentId: id,
userId: actorId, userId: actorId,
}, },
@@ -236,7 +236,7 @@ export async function updateAssignmentUseCase(
quantity, quantity,
itemId, itemId,
assetId: assetId || undefined, assetId: assetId || undefined,
recipientId, personId,
assignmentId: id, assignmentId: id,
userId: actorId, userId: actorId,
}, },
@@ -247,7 +247,7 @@ export async function updateAssignmentUseCase(
id, id,
{ {
createdBy: actorId, createdBy: actorId,
recipientId, recipientId: personId,
itemId, itemId,
assetId, assetId,
quantity, quantity,
@@ -261,7 +261,7 @@ export async function updateAssignmentUseCase(
await AssignmentService.update( await AssignmentService.update(
id, id,
{ {
recipientId, recipientId: personId,
itemId, itemId,
assetId, assetId,
quantity, quantity,
@@ -80,7 +80,7 @@ describe("asset use-cases", () => {
it("creates an assigned asset with assignment and ASSIGNMENT movement", async () => { it("creates an assigned asset with assignment and ASSIGNMENT movement", async () => {
const actor = await createTestUser(prisma) const actor = await createTestUser(prisma)
const recipient = await createTestPerson(prisma) const person = await createTestPerson(prisma)
const item = await createTestItem(prisma, { stock: 0 }) const item = await createTestItem(prisma, { stock: 0 })
const result = await createAssetUseCase({ const result = await createAssetUseCase({
@@ -88,7 +88,7 @@ describe("asset use-cases", () => {
itemId: item.id, itemId: item.id,
serialNumber: "ASSET-ASSIGNED-001", serialNumber: "ASSET-ASSIGNED-001",
status: "ASSIGNED", status: "ASSIGNED",
recipientId: recipient.id, personId: person.id,
}) })
expect(result.success).toBe(true) expect(result.success).toBe(true)
@@ -114,7 +114,7 @@ describe("asset use-cases", () => {
expect(assignment).toMatchObject({ expect(assignment).toMatchObject({
itemId: item.id, itemId: item.id,
assetId: result.assetId, assetId: result.assetId,
recipientId: recipient.id, recipientId: person.id,
quantity: 1, quantity: 1,
createdBy: actor.id, createdBy: actor.id,
returnDate: null, returnDate: null,
@@ -124,7 +124,7 @@ describe("asset use-cases", () => {
type: "ASSIGNMENT", type: "ASSIGNMENT",
itemId: item.id, itemId: item.id,
assetId: result.assetId, assetId: result.assetId,
recipientId: recipient.id, recipientId: person.id,
assignmentId: assignment.id, assignmentId: assignment.id,
quantity: 1, quantity: 1,
userId: actor.id, userId: actor.id,
@@ -133,7 +133,7 @@ describe("asset use-cases", () => {
it("moves an available asset to assigned and back to available", async () => { it("moves an available asset to assigned and back to available", async () => {
const actor = await createTestUser(prisma) const actor = await createTestUser(prisma)
const recipient = await createTestPerson(prisma) const person = await createTestPerson(prisma)
const item = await createTestItem(prisma, { stock: 0 }) const item = await createTestItem(prisma, { stock: 0 })
const created = await createAssetUseCase({ const created = await createAssetUseCase({
@@ -153,7 +153,7 @@ describe("asset use-cases", () => {
itemId: item.id, itemId: item.id,
serialNumber: "ASSET-LIFECYCLE-001", serialNumber: "ASSET-LIFECYCLE-001",
status: "ASSIGNED", status: "ASSIGNED",
recipientId: recipient.id, personId: person.id,
}), }),
).resolves.toEqual({ success: true }) ).resolves.toEqual({ success: true })
@@ -170,7 +170,7 @@ describe("asset use-cases", () => {
expect(activeAssignment).toMatchObject({ expect(activeAssignment).toMatchObject({
itemId: item.id, itemId: item.id,
assetId: created.assetId, assetId: created.assetId,
recipientId: recipient.id, recipientId: person.id,
quantity: 1, quantity: 1,
}) })
@@ -214,7 +214,7 @@ describe("asset use-cases", () => {
expect(movements[1]).toMatchObject({ expect(movements[1]).toMatchObject({
itemId: item.id, itemId: item.id,
assetId: created.assetId, assetId: created.assetId,
recipientId: recipient.id, recipientId: person.id,
assignmentId: activeAssignment.id, assignmentId: activeAssignment.id,
quantity: 1, quantity: 1,
userId: actor.id, userId: actor.id,
@@ -230,7 +230,7 @@ describe("asset use-cases", () => {
it("returns an active assignment without restoring stock when an assigned asset moves to a terminal status", async () => { it("returns an active assignment without restoring stock when an assigned asset moves to a terminal status", async () => {
const actor = await createTestUser(prisma) const actor = await createTestUser(prisma)
const recipient = await createTestPerson(prisma) const person = await createTestPerson(prisma)
const item = await createTestItem(prisma, { stock: 0 }) const item = await createTestItem(prisma, { stock: 0 })
const created = await createAssetUseCase({ const created = await createAssetUseCase({
@@ -238,7 +238,7 @@ describe("asset use-cases", () => {
itemId: item.id, itemId: item.id,
serialNumber: "ASSET-BROKEN-001", serialNumber: "ASSET-BROKEN-001",
status: "ASSIGNED", status: "ASSIGNED",
recipientId: recipient.id, personId: person.id,
}) })
expect(created.success).toBe(true) expect(created.success).toBe(true)
@@ -282,7 +282,7 @@ describe("asset use-cases", () => {
type: "RETURN", type: "RETURN",
itemId: item.id, itemId: item.id,
assetId: created.assetId, assetId: created.assetId,
recipientId: recipient.id, recipientId: person.id,
assignmentId: activeAssignment.id, assignmentId: activeAssignment.id,
quantity: 1, quantity: 1,
userId: actor.id, userId: actor.id,
@@ -38,7 +38,7 @@ afterAll(async () => {
describe("assignment use-cases", () => { describe("assignment use-cases", () => {
it("creates an assignment, decrements stock, and records an ASSIGNMENT movement", async () => { it("creates an assignment, decrements stock, and records an ASSIGNMENT movement", async () => {
const actor = await createTestUser(prisma) const actor = await createTestUser(prisma)
const recipient = await createTestPerson(prisma) const person = await createTestPerson(prisma)
const item = await createTestItem(prisma, { stock: 5 }) const item = await createTestItem(prisma, { stock: 5 })
const assignmentDate = new Date("2026-01-01T00:00:00.000Z") const assignmentDate = new Date("2026-01-01T00:00:00.000Z")
@@ -46,7 +46,7 @@ describe("assignment use-cases", () => {
const result = await createAssignmentUseCase({ const result = await createAssignmentUseCase({
actorId: actor.id, actorId: actor.id,
itemId: item.id, itemId: item.id,
recipientId: recipient.id, personId: person.id,
quantity: 2, quantity: 2,
assignmentDate, assignmentDate,
notes: "Initial assignment", notes: "Initial assignment",
@@ -68,7 +68,7 @@ describe("assignment use-cases", () => {
expect(updatedItem.stock).toBe(3) expect(updatedItem.stock).toBe(3)
expect(assignment).toMatchObject({ expect(assignment).toMatchObject({
itemId: item.id, itemId: item.id,
recipientId: recipient.id, recipientId: person.id,
quantity: 2, quantity: 2,
notes: "Initial assignment", notes: "Initial assignment",
createdBy: actor.id, createdBy: actor.id,
@@ -79,7 +79,7 @@ describe("assignment use-cases", () => {
expect(movements[0]).toMatchObject({ expect(movements[0]).toMatchObject({
type: "ASSIGNMENT", type: "ASSIGNMENT",
itemId: item.id, itemId: item.id,
recipientId: recipient.id, recipientId: person.id,
assignmentId: result.assignmentId, assignmentId: result.assignmentId,
quantity: 2, quantity: 2,
userId: actor.id, userId: actor.id,
@@ -88,13 +88,13 @@ describe("assignment use-cases", () => {
it("rejects assignment creation when item stock is insufficient", async () => { it("rejects assignment creation when item stock is insufficient", async () => {
const actor = await createTestUser(prisma) const actor = await createTestUser(prisma)
const recipient = await createTestPerson(prisma) const person = await createTestPerson(prisma)
const item = await createTestItem(prisma, { stock: 1 }) const item = await createTestItem(prisma, { stock: 1 })
const result = await createAssignmentUseCase({ const result = await createAssignmentUseCase({
actorId: actor.id, actorId: actor.id,
itemId: item.id, itemId: item.id,
recipientId: recipient.id, personId: person.id,
quantity: 2, quantity: 2,
}) })
@@ -114,13 +114,13 @@ describe("assignment use-cases", () => {
it("returns an assignment, restores stock, closes it, and records a RETURN movement", async () => { it("returns an assignment, restores stock, closes it, and records a RETURN movement", async () => {
const actor = await createTestUser(prisma) const actor = await createTestUser(prisma)
const recipient = await createTestPerson(prisma) const person = await createTestPerson(prisma)
const item = await createTestItem(prisma, { stock: 4 }) const item = await createTestItem(prisma, { stock: 4 })
const created = await createAssignmentUseCase({ const created = await createAssignmentUseCase({
actorId: actor.id, actorId: actor.id,
itemId: item.id, itemId: item.id,
recipientId: recipient.id, personId: person.id,
quantity: 3, quantity: 3,
}) })
@@ -172,13 +172,13 @@ describe("assignment use-cases", () => {
it("rejects returning the same assignment twice", async () => { it("rejects returning the same assignment twice", async () => {
const actor = await createTestUser(prisma) const actor = await createTestUser(prisma)
const recipient = await createTestPerson(prisma) const person = await createTestPerson(prisma)
const item = await createTestItem(prisma, { stock: 2 }) const item = await createTestItem(prisma, { stock: 2 })
const created = await createAssignmentUseCase({ const created = await createAssignmentUseCase({
actorId: actor.id, actorId: actor.id,
itemId: item.id, itemId: item.id,
recipientId: recipient.id, personId: person.id,
quantity: 1, quantity: 1,
}) })
+1 -1
View File
@@ -13,7 +13,7 @@ const actionCopy = {
assignmentAlreadyReturned: "La asignación ya fue devuelta", assignmentAlreadyReturned: "La asignación ya fue devuelta",
previousItemNotFound: "Artículo anterior no encontrado", previousItemNotFound: "Artículo anterior no encontrado",
insufficientStock: "El artículo no tiene stock suficiente", insufficientStock: "El artículo no tiene stock suficiente",
recipientRequired: "El destinatario es obligatorio", personRequired: "La persona es obligatoria",
invalidStatus: "Estado inválido", invalidStatus: "Estado inválido",
genericFailure: "Error al procesar el activo", genericFailure: "Error al procesar el activo",
} }
@@ -5,7 +5,7 @@ import { es } from "@/i18n/dictionaries/es"
const mocks = vi.hoisted(() => ({ const mocks = vi.hoisted(() => ({
getI18n: vi.fn(), getI18n: vi.fn(),
findAllRecipients: vi.fn(), findAllPeople: vi.fn(),
findAllItemsWithStock: vi.fn(), findAllItemsWithStock: vi.fn(),
findAllAvailableAssets: vi.fn(), findAllAvailableAssets: vi.fn(),
findAllItems: vi.fn(), findAllItems: vi.fn(),
@@ -25,7 +25,7 @@ vi.mock("@/i18n/server", () => ({
vi.mock("@/services/person.service", () => ({ vi.mock("@/services/person.service", () => ({
PersonService: { PersonService: {
findAll: mocks.findAllRecipients, findAll: mocks.findAllPeople,
}, },
})) }))
@@ -72,9 +72,9 @@ describe("assignment form pages localization", () => {
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks() vi.clearAllMocks()
mocks.getI18n.mockResolvedValue({ dictionary: es, locale: "es" }) mocks.getI18n.mockResolvedValue({ dictionary: es, locale: "es" })
mocks.findAllRecipients.mockResolvedValue([ mocks.findAllPeople.mockResolvedValue([
{ {
id: "recipient-1", id: "person-1",
firstName: "Ada", firstName: "Ada",
lastName: "Lovelace", lastName: "Lovelace",
}, },
@@ -122,10 +122,8 @@ describe("assignment form pages localization", () => {
const html = renderToStaticMarkup(await NewAssignmentPage()) const html = renderToStaticMarkup(await NewAssignmentPage())
expect(html).toContain("Nueva asignación") expect(html).toContain("Nueva asignación")
expect(html).toContain("Destinatario") expect(html).toContain("Persona")
expect(html).toContain( expect(html).toContain('option value="">Selecciona una persona</option>')
'option value="">Selecciona un destinatario</option>',
)
expect(html).toContain("Artículo") expect(html).toContain("Artículo")
expect(html).toContain('option value="">Selecciona un artículo</option>') expect(html).toContain('option value="">Selecciona un artículo</option>')
expect(html).toContain("Cantidad") expect(html).toContain("Cantidad")
@@ -142,7 +140,7 @@ describe("assignment form pages localization", () => {
mocks.findAssignmentById.mockResolvedValue({ mocks.findAssignmentById.mockResolvedValue({
id: "assignment-1", id: "assignment-1",
recipientId: "recipient-1", personId: "person-1",
itemId: "item-1", itemId: "item-1",
assetId: "asset-1", assetId: "asset-1",
quantity: 1, quantity: 1,
@@ -155,7 +153,7 @@ describe("assignment form pages localization", () => {
) )
expect(html).toContain("Editar asignación") expect(html).toContain("Editar asignación")
expect(html).toContain("Destinatario") expect(html).toContain("Persona")
expect(html).toContain("Artículo") expect(html).toContain("Artículo")
expect(html).toContain("Activo") expect(html).toContain("Activo")
expect(html).toContain('option value="">Selecciona un activo</option>') expect(html).toContain('option value="">Selecciona un activo</option>')
@@ -5,7 +5,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"
import { es } from "@/i18n/dictionaries/es" import { es } from "@/i18n/dictionaries/es"
const mocks = vi.hoisted(() => ({ const mocks = vi.hoisted(() => ({
findAllWithRecipientPaginated: vi.fn(), findAllWithPersonPaginated: vi.fn(),
getI18n: vi.fn(), getI18n: vi.fn(),
refresh: vi.fn(), refresh: vi.fn(),
returnAssignment: vi.fn(), returnAssignment: vi.fn(),
@@ -19,7 +19,7 @@ vi.mock("@/i18n/server", () => ({
vi.mock("@/services/assignment.service", () => ({ vi.mock("@/services/assignment.service", () => ({
AssignmentService: { AssignmentService: {
findAllWithRecipientPaginated: mocks.findAllWithRecipientPaginated, findAllWithPersonPaginated: mocks.findAllWithPersonPaginated,
}, },
})) }))
@@ -65,13 +65,13 @@ describe("assignment pages localization", () => {
"@/app/(dashboard)/assignments/page" "@/app/(dashboard)/assignments/page"
) )
mocks.findAllWithRecipientPaginated.mockResolvedValue({ mocks.findAllWithPersonPaginated.mockResolvedValue({
data: [ data: [
{ {
id: "assignment-1", id: "assignment-1",
quantity: 2, quantity: 2,
recipient: { person: {
id: "recipient-1", id: "person-1",
firstName: "Ada", firstName: "Ada",
lastName: "Lovelace", lastName: "Lovelace",
}, },
@@ -93,7 +93,7 @@ describe("assignment pages localization", () => {
expect(html).toContain("Asignaciones") expect(html).toContain("Asignaciones")
expect(html).toContain("Agregar asignación") expect(html).toContain("Agregar asignación")
expect(html).toContain("Destinatario") expect(html).toContain("Persona")
expect(html).toContain("Artículo") expect(html).toContain("Artículo")
expect(html).toContain("Número de serie") expect(html).toContain("Número de serie")
expect(html).toContain("Cantidad") expect(html).toContain("Cantidad")
@@ -110,7 +110,7 @@ describe("assignment pages localization", () => {
"@/app/(dashboard)/assignments/page" "@/app/(dashboard)/assignments/page"
) )
mocks.findAllWithRecipientPaginated.mockResolvedValue({ mocks.findAllWithPersonPaginated.mockResolvedValue({
data: [], data: [],
totalPages: 0, totalPages: 0,
}) })
@@ -68,7 +68,7 @@ describe("movement pages localization", () => {
createdAt: new Date("2026-06-13T00:00:00.000Z"), createdAt: new Date("2026-06-13T00:00:00.000Z"),
item: { name: "Laptop" }, item: { name: "Laptop" },
asset: { serialNumber: "SN-001" }, asset: { serialNumber: "SN-001" },
recipient: { firstName: "Ada", lastName: "Lovelace" }, person: { firstName: "Ada", lastName: "Lovelace" },
}, },
], ],
totalPages: 1, totalPages: 1,
@@ -83,7 +83,7 @@ describe("movement pages localization", () => {
expect(html).toContain("Artículo") expect(html).toContain("Artículo")
expect(html).toContain("Número de serie") expect(html).toContain("Número de serie")
expect(html).toContain("Cantidad") expect(html).toContain("Cantidad")
expect(html).toContain("Destinatario") expect(html).toContain("Persona")
expect(html).toContain("Fecha") expect(html).toContain("Fecha")
expect(html).toContain("Asignación") expect(html).toContain("Asignación")
expect(html).toContain("Laptop") expect(html).toContain("Laptop")
+16 -16
View File
@@ -415,7 +415,7 @@ describe("i18n dictionaries", () => {
addLabel: "Add Assignment", addLabel: "Add Assignment",
empty: "No assignments found.", empty: "No assignments found.",
columns: { columns: {
recipient: "Recipient", person: "Person",
item: "Item", item: "Item",
serialNumber: "Serial Number", serialNumber: "Serial Number",
quantity: "Quantity", quantity: "Quantity",
@@ -434,8 +434,8 @@ describe("i18n dictionaries", () => {
notFound: "Assignment not found", notFound: "Assignment not found",
}, },
form: { form: {
recipientLabel: "Recipient", personLabel: "Person",
recipientPlaceholder: "Select a recipient", personPlaceholder: "Select a person",
itemLabel: "Item", itemLabel: "Item",
itemPlaceholder: "Select an item", itemPlaceholder: "Select an item",
assetLabel: "Asset", assetLabel: "Asset",
@@ -465,7 +465,7 @@ describe("i18n dictionaries", () => {
genericFailure: "Error processing assignment", genericFailure: "Error processing assignment",
}, },
schema: { schema: {
recipientRequired: "Recipient is required", personRequired: "Person is required",
itemIdRequired: "Item is required", itemIdRequired: "Item is required",
quantityMinOne: "Quantity must be at least 1", quantityMinOne: "Quantity must be at least 1",
assetIdRequired: "Asset ID is required when item ID is provided", assetIdRequired: "Asset ID is required when item ID is provided",
@@ -479,7 +479,7 @@ describe("i18n dictionaries", () => {
addLabel: "Agregar asignación", addLabel: "Agregar asignación",
empty: "No se encontraron asignaciones.", empty: "No se encontraron asignaciones.",
columns: { columns: {
recipient: "Destinatario", person: "Persona",
item: "Artículo", item: "Artículo",
serialNumber: "Número de serie", serialNumber: "Número de serie",
quantity: "Cantidad", quantity: "Cantidad",
@@ -498,8 +498,8 @@ describe("i18n dictionaries", () => {
notFound: "Asignación no encontrada", notFound: "Asignación no encontrada",
}, },
form: { form: {
recipientLabel: "Destinatario", personLabel: "Persona",
recipientPlaceholder: "Selecciona un destinatario", personPlaceholder: "Selecciona una persona",
itemLabel: "Artículo", itemLabel: "Artículo",
itemPlaceholder: "Selecciona un artículo", itemPlaceholder: "Selecciona un artículo",
assetLabel: "Activo", assetLabel: "Activo",
@@ -529,7 +529,7 @@ describe("i18n dictionaries", () => {
genericFailure: "Error al procesar la asignación", genericFailure: "Error al procesar la asignación",
}, },
schema: { schema: {
recipientRequired: "El destinatario es obligatorio", personRequired: "La persona es obligatoria",
itemIdRequired: "El artículo es obligatorio", itemIdRequired: "El artículo es obligatorio",
quantityMinOne: "La cantidad debe ser al menos 1", quantityMinOne: "La cantidad debe ser al menos 1",
assetIdRequired: assetIdRequired:
@@ -572,8 +572,8 @@ describe("i18n dictionaries", () => {
deliveryNotePlaceholder: "Delivery note", deliveryNotePlaceholder: "Delivery note",
statusLabel: "Status", statusLabel: "Status",
statusPlaceholder: "Select a status", statusPlaceholder: "Select a status",
recipientLabel: "Recipient", personLabel: "Person",
recipientPlaceholder: "Select a recipient", personPlaceholder: "Select a person",
createSubmit: "Create Asset", createSubmit: "Create Asset",
updateSubmit: "Update Asset", updateSubmit: "Update Asset",
}, },
@@ -600,7 +600,7 @@ describe("i18n dictionaries", () => {
assignmentAlreadyReturned: "Assignment already returned", assignmentAlreadyReturned: "Assignment already returned",
previousItemNotFound: "Previous item not found for available asset", previousItemNotFound: "Previous item not found for available asset",
insufficientStock: "Item does not have enough stock", insufficientStock: "Item does not have enough stock",
recipientRequired: "Recipient is required", personRequired: "Person is required",
invalidStatus: "Invalid status", invalidStatus: "Invalid status",
genericFailure: "Error processing asset", genericFailure: "Error processing asset",
}, },
@@ -646,8 +646,8 @@ describe("i18n dictionaries", () => {
deliveryNotePlaceholder: "Remito", deliveryNotePlaceholder: "Remito",
statusLabel: "Estado", statusLabel: "Estado",
statusPlaceholder: "Selecciona un estado", statusPlaceholder: "Selecciona un estado",
recipientLabel: "Destinatario", personLabel: "Persona",
recipientPlaceholder: "Selecciona un destinatario", personPlaceholder: "Selecciona una persona",
createSubmit: "Crear activo", createSubmit: "Crear activo",
updateSubmit: "Actualizar activo", updateSubmit: "Actualizar activo",
}, },
@@ -675,7 +675,7 @@ describe("i18n dictionaries", () => {
previousItemNotFound: previousItemNotFound:
"Artículo anterior no encontrado para el activo disponible", "Artículo anterior no encontrado para el activo disponible",
insufficientStock: "El artículo no tiene stock suficiente", insufficientStock: "El artículo no tiene stock suficiente",
recipientRequired: "El destinatario es obligatorio", personRequired: "La persona es obligatoria",
invalidStatus: "Estado inválido", invalidStatus: "Estado inválido",
genericFailure: "Error al procesar el activo", genericFailure: "Error al procesar el activo",
}, },
@@ -865,7 +865,7 @@ describe("i18n dictionaries", () => {
item: "Item", item: "Item",
serialNumber: "Serial Number", serialNumber: "Serial Number",
quantity: "Quantity", quantity: "Quantity",
recipient: "Recipient", person: "Person",
date: "Date", date: "Date",
}, },
}, },
@@ -899,7 +899,7 @@ describe("i18n dictionaries", () => {
item: "Artículo", item: "Artículo",
serialNumber: "Número de serie", serialNumber: "Número de serie",
quantity: "Cantidad", quantity: "Cantidad",
recipient: "Destinatario", person: "Persona",
date: "Fecha", date: "Fecha",
}, },
}, },
+1 -1
View File
@@ -54,7 +54,7 @@ describe("asset schema localization", () => {
itemId: "item-1", itemId: "item-1",
serialNumber: "SERIAL-1", serialNumber: "SERIAL-1",
status: "ASSIGNED", status: "ASSIGNED",
recipientId: "recipient-1", personId: "person-1",
}) })
expect(createResult.success).toBe(true) expect(createResult.success).toBe(true)
+9 -9
View File
@@ -6,7 +6,7 @@ import {
} from "@/schemas/assignment.schema" } from "@/schemas/assignment.schema"
const schemaCopy = { const schemaCopy = {
recipientRequired: "El destinatario es obligatorio", personRequired: "La persona es obligatoria",
itemIdRequired: "El artículo es obligatorio", itemIdRequired: "El artículo es obligatorio",
quantityMinOne: "La cantidad debe ser al menos 1", quantityMinOne: "La cantidad debe ser al menos 1",
assetIdRequired: "El activo es obligatorio cuando se especifica el artículo", assetIdRequired: "El activo es obligatorio cuando se especifica el artículo",
@@ -16,7 +16,7 @@ const schemaCopy = {
describe("assignment schema localization", () => { describe("assignment schema localization", () => {
it("uses localized create validation messages for missing required fields", () => { it("uses localized create validation messages for missing required fields", () => {
const result = buildCreateAssignmentSchema(schemaCopy).safeParse({ const result = buildCreateAssignmentSchema(schemaCopy).safeParse({
recipientId: "", personId: "",
quantity: 0, quantity: 0,
}) })
@@ -24,7 +24,7 @@ describe("assignment schema localization", () => {
if (!result.success) { if (!result.success) {
const errors = result.error.flatten().fieldErrors const errors = result.error.flatten().fieldErrors
expect(errors.recipientId).toContain(schemaCopy.recipientRequired) expect(errors.personId).toContain(schemaCopy.personRequired)
expect(errors.quantity).toContain(schemaCopy.quantityMinOne) expect(errors.quantity).toContain(schemaCopy.quantityMinOne)
} }
}) })
@@ -33,7 +33,7 @@ describe("assignment schema localization", () => {
const result = buildUpdateAssignmentSchema(schemaCopy).safeParse({ const result = buildUpdateAssignmentSchema(schemaCopy).safeParse({
id: "", id: "",
itemId: "item-1", itemId: "item-1",
recipientId: "recipient-1", personId: "person-1",
quantity: 1, quantity: 1,
assetId: "", assetId: "",
}) })
@@ -49,20 +49,20 @@ describe("assignment schema localization", () => {
it("preserves valid create and update payloads without errors", () => { it("preserves valid create and update payloads without errors", () => {
const createResult = buildCreateAssignmentSchema(schemaCopy).safeParse({ const createResult = buildCreateAssignmentSchema(schemaCopy).safeParse({
recipientId: "recipient-1", personId: "person-1",
itemId: "item-1", itemId: "item-1",
quantity: 2, quantity: 2,
}) })
expect(createResult.success).toBe(true) expect(createResult.success).toBe(true)
if (createResult.success) { if (createResult.success) {
expect(createResult.data.recipientId).toBe("recipient-1") expect(createResult.data.personId).toBe("person-1")
expect(createResult.data.quantity).toBe(2) expect(createResult.data.quantity).toBe(2)
} }
const updateResult = buildUpdateAssignmentSchema(schemaCopy).safeParse({ const updateResult = buildUpdateAssignmentSchema(schemaCopy).safeParse({
id: "assignment-1", id: "assignment-1",
recipientId: "recipient-1", personId: "person-1",
itemId: "item-1", itemId: "item-1",
assetId: "asset-1", assetId: "asset-1",
quantity: 1, quantity: 1,
@@ -76,7 +76,7 @@ describe("assignment schema localization", () => {
it("keeps optional assignment fields optional in create", () => { it("keeps optional assignment fields optional in create", () => {
const result = buildCreateAssignmentSchema(schemaCopy).safeParse({ const result = buildCreateAssignmentSchema(schemaCopy).safeParse({
recipientId: "recipient-1", personId: "person-1",
quantity: 1, quantity: 1,
}) })
@@ -86,7 +86,7 @@ describe("assignment schema localization", () => {
it("allows update without itemId when no assetId is required", () => { it("allows update without itemId when no assetId is required", () => {
const result = buildUpdateAssignmentSchema(schemaCopy).safeParse({ const result = buildUpdateAssignmentSchema(schemaCopy).safeParse({
id: "assignment-1", id: "assignment-1",
recipientId: "recipient-1", personId: "person-1",
quantity: 1, quantity: 1,
}) })
+2 -2
View File
@@ -21,7 +21,7 @@ describe("core schemas", () => {
expect( expect(
createAssignmentSchema.safeParse({ createAssignmentSchema.safeParse({
itemId: "item-id", itemId: "item-id",
recipientId: "recipient-id", personId: "person-id",
quantity: "2", quantity: "2",
}), }),
).toMatchObject({ success: true, data: { quantity: 2 } }) ).toMatchObject({ success: true, data: { quantity: 2 } })
@@ -47,7 +47,7 @@ describe("core schemas", () => {
expect( expect(
createAssignmentSchema.safeParse({ createAssignmentSchema.safeParse({
itemId: "item-id", itemId: "item-id",
recipientId: "recipient-id", personId: "person-id",
quantity: 0, quantity: 0,
}).success, }).success,
).toBe(false) ).toBe(false)