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