feat(jobs): clinic-side 'Düzeltme İste' (revision request) flow
Up to now the only thing a clinic could do on a prova was approve it.
If the casting didn't fit there was no way to bounce the case back to
the lab short of cancelling the whole thing. Real-world flow needs a
'try again, this is what's wrong' lever, so:
- requestRevisionAction (clinic only): pre-conditions
in_progress + at_clinic + currentStep set; flips location → at_lab
while leaving currentStep untouched so the same prova stage repeats
after the lab redoes the work. Requires a note (the lab can't fix
what it doesn't know is broken) — appended to job_status_history
with a '[Düzeltme talebi]' prefix and surfaced to the lab via
notification.
- JobActionsPanel: when the clinic side sees a prova (in_progress +
at_clinic) it now shows two buttons — Onayla as before, plus
Düzeltme İste (variant=destructive). The dialog requires a note
before submit.
This commit is contained in:
@@ -522,6 +522,95 @@ export async function approveAtClinicAction(
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Clinic rejects the prova and asks the lab to redo this stage. The job
|
||||
* goes back to the lab without advancing the step, so the same prova
|
||||
* stage will repeat after the lab finishes the rework. A note explaining
|
||||
* what's wrong is required — there's no point bouncing a case back
|
||||
* without telling the lab what to fix.
|
||||
*/
|
||||
export async function requestRevisionAction(
|
||||
_prev: JobActionState,
|
||||
formData: FormData,
|
||||
): Promise<JobActionState> {
|
||||
const jobId = String(formData.get("jobId") ?? "").trim();
|
||||
if (!jobId) return { ok: false, error: "İş bulunamadı." };
|
||||
const note = String(formData.get("note") ?? "").trim();
|
||||
if (!note) {
|
||||
return {
|
||||
ok: false,
|
||||
error: "Düzeltme talebi için neyin yanlış olduğunu yazmanız gerek.",
|
||||
};
|
||||
}
|
||||
|
||||
let ctx;
|
||||
try {
|
||||
ctx = await requireTenant();
|
||||
requireRole(ctx, ["owner", "admin", "member"]);
|
||||
requireTenantKind(ctx, ["clinic"]);
|
||||
} catch {
|
||||
return { ok: false, error: "Düzeltme talebini yalnızca klinik açabilir." };
|
||||
}
|
||||
|
||||
const job = await loadJobForTenant(jobId, ctx.tenantId);
|
||||
if (!job || job.clinicTenantId !== ctx.tenantId) {
|
||||
return { ok: false, error: "İş bulunamadı." };
|
||||
}
|
||||
if (job.status !== "in_progress") {
|
||||
return { ok: false, error: "Yalnızca işlemdeki provalar için düzeltme istenebilir." };
|
||||
}
|
||||
if (job.location !== "at_clinic") {
|
||||
return { ok: false, error: "İş şu an klinikte değil." };
|
||||
}
|
||||
if (!job.currentStep) {
|
||||
return { ok: false, error: "Mevcut aşama bilinmiyor." };
|
||||
}
|
||||
|
||||
try {
|
||||
const { tablesDB } = createAdminClient();
|
||||
await tablesDB.updateRow(DATABASE_ID, TABLES.jobs, jobId, {
|
||||
location: "at_lab",
|
||||
// currentStep stays the same — the lab will rework this stage.
|
||||
});
|
||||
await appendJobHistory({
|
||||
job,
|
||||
step: job.currentStep,
|
||||
completedBy: ctx.user.id,
|
||||
note: `[Düzeltme talebi] ${note}`,
|
||||
});
|
||||
void logAudit({
|
||||
tenantId: ctx.tenantId,
|
||||
userId: ctx.user.id,
|
||||
action: "update",
|
||||
entityType: "job",
|
||||
entityId: jobId,
|
||||
changes: {
|
||||
location: "at_lab",
|
||||
revisionRequestedAtStep: job.currentStep,
|
||||
note,
|
||||
},
|
||||
});
|
||||
const stepLabel =
|
||||
job.currentStep === "alt_yapi_prova"
|
||||
? "alt yapı"
|
||||
: job.currentStep === "ust_yapi_prova"
|
||||
? "üst yapı"
|
||||
: "cila/bitim";
|
||||
void createNotification({
|
||||
tenantId: job.labTenantId,
|
||||
jobId,
|
||||
message: `Hasta ${job.patientCode} ${stepLabel} provası için düzeltme istendi: ${note.slice(0, 120)}`,
|
||||
});
|
||||
} catch (e) {
|
||||
return { ok: false, error: appwriteError(e, "Düzeltme talebi gönderilemedi.") };
|
||||
}
|
||||
|
||||
revalidatePath(`/jobs/${jobId}`);
|
||||
revalidatePath("/jobs/inbound");
|
||||
revalidatePath("/jobs/outbound");
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export async function markDeliveredAction(
|
||||
_prev: JobActionState,
|
||||
formData: FormData,
|
||||
|
||||
Reference in New Issue
Block a user