PO fix
This commit is contained in:
@@ -5,6 +5,14 @@
|
|||||||
import { timestampToDateTime } from "$lib/utils/conversion";
|
import { timestampToDateTime } from "$lib/utils/conversion";
|
||||||
import { getSessionAuthId } from "$lib/utils/authUtil";
|
import { getSessionAuthId } from "$lib/utils/authUtil";
|
||||||
|
|
||||||
|
|
||||||
|
function ordinal(num: number) {
|
||||||
|
if (num === 1) return "1st";
|
||||||
|
if (num === 2) return "2nd";
|
||||||
|
if (num === 3) return "3rd";
|
||||||
|
return `${num}th`;
|
||||||
|
}
|
||||||
|
|
||||||
type PurchaseOrderInsert = {
|
type PurchaseOrderInsert = {
|
||||||
issue_id: string;
|
issue_id: string;
|
||||||
prepared_date: string;
|
prepared_date: string;
|
||||||
@@ -70,6 +78,7 @@
|
|||||||
acknowledged: boolean;
|
acknowledged: boolean;
|
||||||
acknowledge_by: string;
|
acknowledge_by: string;
|
||||||
approved_by: string;
|
approved_by: string;
|
||||||
|
prepared: boolean | null;
|
||||||
approved_price: number;
|
approved_price: number;
|
||||||
approved_quantity: number;
|
approved_quantity: number;
|
||||||
total_approved_order_amount: number;
|
total_approved_order_amount: number;
|
||||||
@@ -94,35 +103,21 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const columns: columns[] = [
|
const columns: columns[] = [
|
||||||
{ key: "name", title: "Name" },
|
{ key: "issue_name", title: "Issue Name" },
|
||||||
{ key: "purchase_order_number", title: "Purchase Order Number" },
|
{ key: "requested_date", title: "Requested Date" },
|
||||||
{ key: "villa_name", title: "Villa Name" },
|
{ key: "purchase_order_number", title: "PO Number" },
|
||||||
{ key: "priority", title: "Priority" },
|
|
||||||
{ key: "prepared_date", title: "Prepared Date" },
|
|
||||||
{ key: "po_type", title: "PO Type" },
|
|
||||||
{ key: "po_quantity", title: "PO Quantity" },
|
|
||||||
{ key: "po_price", title: "PO Price" },
|
|
||||||
{ key: "po_total_price", title: "PO Total Price" },
|
|
||||||
{ key: "po_status", title: "PO Status" },
|
{ key: "po_status", title: "PO Status" },
|
||||||
{ key: "proses_to_approval", title: "PROSES TO APPROVAL" },
|
{ key: "villa_data", title: "Villa Name" },
|
||||||
{ key: "approved_vendor", title: "Approved Vendor" },
|
{ key: "po_item", title: "PO Product" },
|
||||||
{ key: "acknowledged", title: "Acknowledged" },
|
{ key: "po_remark", title: "PO Remark" },
|
||||||
{ key: "acknowledged_name", title: "Acknowledged By" },
|
{ key: "po_due", title: "PO Due" },
|
||||||
{ key: "approved_name", title: "Approved By" },
|
{ key: "prepared", title: "Prepare PO" },
|
||||||
{ key: "approved_price", title: "Approved Price" },
|
{ key: "approved", title: "Approval" },
|
||||||
{ key: "approved_quantity", title: "Approved Quantity" },
|
{ key: "acknowledged", title: "Acknowledge" },
|
||||||
{
|
{ key: "completed", title: "Complete" },
|
||||||
key: "total_approved_order_amount",
|
{ key: "received", title: "Receive" },
|
||||||
title: "Total Approved Order Amount",
|
|
||||||
},
|
|
||||||
{ key: "approval", title: "Approval" },
|
|
||||||
{ key: "completed_status", title: "Completed Status" },
|
|
||||||
{ key: "received", title: "Received" },
|
|
||||||
{ key: "received_name", title: "Received By" },
|
|
||||||
{ key: "inputed_name", title: "Input By" },
|
|
||||||
{ key: "updated_name", title: "Updated By" },
|
|
||||||
{ key: "updated_at", title: "Updated At" },
|
{ key: "updated_at", title: "Updated At" },
|
||||||
{ key: "created_at", title: "Created At" },
|
{ key: "payment", title: "Payment" },
|
||||||
{ key: "actions", title: "Actions" }, // For edit/delete buttons
|
{ key: "actions", title: "Actions" }, // For edit/delete buttons
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -138,6 +133,94 @@
|
|||||||
if (page >= 1 && page <= totalPages) currentPage = page;
|
if (page >= 1 && page <= totalPages) currentPage = page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let showPaymentModal = false;
|
||||||
|
let paymentForm = {
|
||||||
|
payment_method: "",
|
||||||
|
total_approved_order_amount: 0,
|
||||||
|
"1st_pay_amt": 0,
|
||||||
|
"2nd_pay_amt": 0,
|
||||||
|
"3rd_pay_amt": 0,
|
||||||
|
"4th_pay_amt": 0,
|
||||||
|
"5th_pay_amt": 0,
|
||||||
|
"6th_pay_amt": 0,
|
||||||
|
"1st_pay_date": "",
|
||||||
|
"2nd_pay_date": "",
|
||||||
|
"3rd_pay_date": "",
|
||||||
|
"4th_pay_date": "",
|
||||||
|
"5th_pay_date": "",
|
||||||
|
"6th_pay_date": "",
|
||||||
|
due_remaining: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let showPreparedModal = false;
|
||||||
|
let selectedPO = null;
|
||||||
|
|
||||||
|
let preparedByOptions: any[] = [];
|
||||||
|
let poItemOptions: any[] = [];
|
||||||
|
let vendorOptions: any[] = [];
|
||||||
|
|
||||||
|
let preparedForm = {
|
||||||
|
prepared_by: "",
|
||||||
|
prepared_date: "",
|
||||||
|
po_item: "",
|
||||||
|
po_quantity: 1,
|
||||||
|
q1_vendor: "",
|
||||||
|
q2_vendor: "",
|
||||||
|
q3_vendor: "",
|
||||||
|
q1_vendor_price: 0,
|
||||||
|
q2_vendor_price: 0,
|
||||||
|
q3_vendor_price: 0,
|
||||||
|
po_remark: "",
|
||||||
|
approved_quantity: 0,
|
||||||
|
approved_vendor: "",
|
||||||
|
approved_price: 0,
|
||||||
|
total_approved_order_amount: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
async function openPreparedModal(row) {
|
||||||
|
selectedPO = row;
|
||||||
|
|
||||||
|
// Prefill existing values
|
||||||
|
preparedForm = {
|
||||||
|
prepared_by: row.prepared_by || "",
|
||||||
|
prepared_date: row.prepared_date || "",
|
||||||
|
po_item: row.po_item || "",
|
||||||
|
po_quantity: row.po_quantity || 1,
|
||||||
|
q1_vendor: row.q1_vendor || "",
|
||||||
|
q2_vendor: row.q2_vendor || "",
|
||||||
|
q3_vendor: row.q3_vendor || "",
|
||||||
|
q1_vendor_price: row.q1_vendor_price || 0,
|
||||||
|
q2_vendor_price: row.q2_vendor_price || 0,
|
||||||
|
q3_vendor_price: row.q3_vendor_price || 0,
|
||||||
|
po_remark: row.po_remark || "",
|
||||||
|
approved_quantity: row.approved_quantity || 0,
|
||||||
|
approved_vendor: row.approved_vendor || "",
|
||||||
|
approved_price: row.approved_price || 0,
|
||||||
|
total_approved_order_amount: row.approved_quantity * row.approved_price
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load dropdowns
|
||||||
|
await fetchPreparedDropdowns();
|
||||||
|
|
||||||
|
showPreparedModal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchPreparedDropdowns() {
|
||||||
|
const [{ data: employees }, { data: items }, { data: vendors }] = await Promise.all([
|
||||||
|
supabase.from("vb_employee").select("id, employee_name").eq("employee_status", "Active"),
|
||||||
|
supabase.from("vb_po_item").select("id, item_name"),
|
||||||
|
supabase.from("vb_vendor").select("id, name")
|
||||||
|
]);
|
||||||
|
|
||||||
|
preparedByOptions = employees || [];
|
||||||
|
poItemOptions = items || [];
|
||||||
|
vendorOptions = vendors || [];
|
||||||
|
}
|
||||||
async function fetchPurchaseOrder(
|
async function fetchPurchaseOrder(
|
||||||
filter: string | null = null,
|
filter: string | null = null,
|
||||||
search: string | null = null,
|
search: string | null = null,
|
||||||
@@ -292,6 +375,7 @@
|
|||||||
(col) => !excludedKeys.includes(col.key),
|
(col) => !excludedKeys.includes(col.key),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
async function openModal(purchase: PurchaseOrderDisplay | null = null) {
|
async function openModal(purchase: PurchaseOrderDisplay | null = null) {
|
||||||
await fetchIssues();
|
await fetchIssues();
|
||||||
if (purchase) {
|
if (purchase) {
|
||||||
@@ -306,6 +390,65 @@
|
|||||||
showModal = true;
|
showModal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateTotalAmount() {
|
||||||
|
preparedForm.total_approved_order_amount =
|
||||||
|
preparedForm.approved_quantity * preparedForm.approved_price;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function savePrepared() {
|
||||||
|
const { error } = await supabase
|
||||||
|
.from("vb_purchase_orders")
|
||||||
|
.update({
|
||||||
|
prepared_by: preparedForm.prepared_by,
|
||||||
|
prepared_date: preparedForm.prepared_date,
|
||||||
|
po_item: preparedForm.po_item,
|
||||||
|
po_quantity: preparedForm.po_quantity,
|
||||||
|
q1_vendor: preparedForm.q1_vendor,
|
||||||
|
q2_vendor: preparedForm.q2_vendor,
|
||||||
|
q3_vendor: preparedForm.q3_vendor,
|
||||||
|
q1_vendor_price: preparedForm.q1_vendor_price,
|
||||||
|
q2_vendor_price: preparedForm.q2_vendor_price,
|
||||||
|
q3_vendor_price: preparedForm.q3_vendor_price,
|
||||||
|
approved_quantity: preparedForm.approved_quantity,
|
||||||
|
approved_vendor: preparedForm.approved_vendor,
|
||||||
|
approved_price: preparedForm.approved_price,
|
||||||
|
total_approved_order_amount: preparedForm.total_approved_order_amount,
|
||||||
|
po_remark: preparedForm.po_remark,
|
||||||
|
po_status: "prepared",
|
||||||
|
prepared: true,
|
||||||
|
})
|
||||||
|
.eq("id", selectedPO.id);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error("Error saving prepared data:", error);
|
||||||
|
alert("Failed to save.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fetch("https://flow.catalis.app/webhook/vb_approval_po_new", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
po_id: selectedPO.id,
|
||||||
|
po_status: "prepared",
|
||||||
|
prepared_by: preparedForm.prepared_by,
|
||||||
|
prepared_date: preparedForm.prepared_date,
|
||||||
|
po_item: preparedForm.po_item,
|
||||||
|
po_quantity: preparedForm.po_quantity,
|
||||||
|
approved_quantity: preparedForm.approved_quantity,
|
||||||
|
approved_vendor: preparedForm.approved_vendor,
|
||||||
|
approved_price: preparedForm.approved_price,
|
||||||
|
total_approved_order_amount: preparedForm.total_approved_order_amount,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} catch (webhookError) {
|
||||||
|
console.error("Failed to fire webhook:", webhookError);
|
||||||
|
alert("Save worked but webhook failed.");
|
||||||
|
}
|
||||||
|
showPreparedModal = false;
|
||||||
|
await fetchPurchaseOrder();
|
||||||
|
}
|
||||||
async function saveProject() {
|
async function saveProject() {
|
||||||
// session check
|
// session check
|
||||||
const session = await supabase.auth.getSession();
|
const session = await supabase.auth.getSession();
|
||||||
@@ -618,6 +761,84 @@
|
|||||||
|
|
||||||
await fetchPurchaseOrder();
|
await fetchPurchaseOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openPaymentModal(row) {
|
||||||
|
selectedPO = row;
|
||||||
|
|
||||||
|
paymentForm = {
|
||||||
|
payment_method: row.payment_method || "",
|
||||||
|
total_approved_order_amount: row.total_approved_order_amount || 0,
|
||||||
|
"1st_pay_amt": row["1st_pay_amt"] || 0,
|
||||||
|
"2nd_pay_amt": row["2nd_pay_amt"] || 0,
|
||||||
|
"3rd_pay_amt": row["3rd_pay_amt"] || 0,
|
||||||
|
"4th_pay_amt": row["4th_pay_amt"] || 0,
|
||||||
|
"5th_pay_amt": row["5th_pay_amt"] || 0,
|
||||||
|
"6th_pay_amt": row["6th_pay_amt"] || 0,
|
||||||
|
"1st_pay_date": row["1st_pay_date"] || "",
|
||||||
|
"2nd_pay_date": row["2nd_pay_date"] || "",
|
||||||
|
"3rd_pay_date": row["3rd_pay_date"] || "",
|
||||||
|
"4th_pay_date": row["4th_pay_date"] || "",
|
||||||
|
"5th_pay_date": row["5th_pay_date"] || "",
|
||||||
|
"6th_pay_date": row["6th_pay_date"] || "",
|
||||||
|
due_remaining: calculateDueRemaining(row)
|
||||||
|
};
|
||||||
|
|
||||||
|
showPaymentModal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateDueRemaining(source) {
|
||||||
|
const total = source.total_approved_order_amount || 0;
|
||||||
|
const sum =
|
||||||
|
(source["1st_pay_amt"] || 0) +
|
||||||
|
(source["2nd_pay_amt"] || 0) +
|
||||||
|
(source["3rd_pay_amt"] || 0) +
|
||||||
|
(source["4th_pay_amt"] || 0) +
|
||||||
|
(source["5th_pay_amt"] || 0) +
|
||||||
|
(source["6th_pay_amt"] || 0);
|
||||||
|
return total - sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDueRemaining() {
|
||||||
|
paymentForm.due_remaining =
|
||||||
|
paymentForm.total_approved_order_amount -
|
||||||
|
paymentForm["1st_pay_amt"] -
|
||||||
|
paymentForm["2nd_pay_amt"] -
|
||||||
|
paymentForm["3rd_pay_amt"] -
|
||||||
|
paymentForm["4th_pay_amt"] -
|
||||||
|
paymentForm["5th_pay_amt"] -
|
||||||
|
paymentForm["6th_pay_amt"];
|
||||||
|
}
|
||||||
|
async function savePayment() {
|
||||||
|
const { error } = await supabase
|
||||||
|
.from("vb_purchase_orders")
|
||||||
|
.update({
|
||||||
|
payment_method: paymentForm.payment_method,
|
||||||
|
"1st_pay_amt": paymentForm["1st_pay_amt"],
|
||||||
|
"2nd_pay_amt": paymentForm["2nd_pay_amt"],
|
||||||
|
"3rd_pay_amt": paymentForm["3rd_pay_amt"],
|
||||||
|
"4th_pay_amt": paymentForm["4th_pay_amt"],
|
||||||
|
"5th_pay_amt": paymentForm["5th_pay_amt"],
|
||||||
|
"6th_pay_amt": paymentForm["6th_pay_amt"],
|
||||||
|
"1st_pay_date": paymentForm["1st_pay_date"] || null,
|
||||||
|
"2nd_pay_date": paymentForm["2nd_pay_date"] || null,
|
||||||
|
"3rd_pay_date": paymentForm["3rd_pay_date"] || null,
|
||||||
|
"4th_pay_date": paymentForm["4th_pay_date"] || null,
|
||||||
|
"5th_pay_date": paymentForm["5th_pay_date"] || null,
|
||||||
|
"6th_pay_date": paymentForm["6th_pay_date"] || null,
|
||||||
|
due_remaining: paymentForm.due_remaining
|
||||||
|
})
|
||||||
|
.eq("id", selectedPO.id);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error("Error saving payment data:", error);
|
||||||
|
alert("Failed to save.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showPaymentModal = false;
|
||||||
|
await fetchPurchaseOrder();
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -691,201 +912,63 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-gray-200 bg-white">
|
<tbody class="divide-y divide-gray-200 bg-white">
|
||||||
{#each paginatedRows as row}
|
{#each paginatedRows as row}
|
||||||
<tr class="hover:bg-gray-50 transition">
|
<tr class="hover:bg-gray-50 transition">
|
||||||
{#each columns as col}
|
{#each columns as col}
|
||||||
{#if col.key === "name"}
|
{#if col.key === "requested_date" || col.key === "po_due" || col.key === "updated_at"}
|
||||||
<td
|
<td class="px-4 py-2 text-gray-500">
|
||||||
class="sticky left-0 px-4 py-2 font-medium text-blue-600"
|
{#if row[col.key]}
|
||||||
style="background-color: #f0f8ff; cursor: pointer;"
|
{new Date(row[col.key]).toLocaleString()}
|
||||||
>
|
{:else}
|
||||||
{row[col.key as keyof PurchaseOrderDisplay]}
|
—
|
||||||
</td>
|
{/if}
|
||||||
{:else if col.key === "approval"}
|
</td>
|
||||||
<td class="px-4 py-2">
|
{:else if col.key === "prepared"}
|
||||||
<select
|
<td class="px-4 py-2 text-center">
|
||||||
bind:value={
|
<button
|
||||||
row[
|
class="bg-blue-600 text-white text-xs px-3 py-1.5 rounded hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||||
col.key as keyof PurchaseOrderDisplay
|
on:click={() => openPreparedModal(row)}
|
||||||
]
|
disabled={row.prepared === true }
|
||||||
}
|
>
|
||||||
class="w-full p-3 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500 text-gray-900"
|
{row.prepared === true ? "Prepared" : "Set Prepared"}
|
||||||
on:change={(e: Event) => {
|
</button>
|
||||||
updatePurchaseOrderApprovalStatus(
|
</td>
|
||||||
e,
|
{:else if col.key === "approved" || col.key === "acknowledged" || col.key === "completed" || col.key === "received"}
|
||||||
row.id,
|
<td class="px-4 py-2 text-center">
|
||||||
row,
|
<input type="checkbox" checked={row[col.key]} disabled />
|
||||||
);
|
</td>
|
||||||
}}
|
{:else if col.key === "payment"}
|
||||||
disabled
|
<td class="px-4 py-2 text-center">
|
||||||
>
|
<button
|
||||||
<option value="" disabled selected
|
class="inline-flex items-center gap-1 rounded bg-emerald-600 px-3 py-1.5 text-white text-xs font-medium hover:bg-emerald-700"
|
||||||
>ON PROSES</option
|
on:click={() => openPaymentModal(row)}
|
||||||
>
|
>
|
||||||
<option value="APPROVED"
|
Payment
|
||||||
>APPROVED</option
|
</button>
|
||||||
>
|
</td>
|
||||||
<option value="REJECTED"
|
{:else if col.key === "actions"}
|
||||||
>REJECTED</option
|
<td class="px-4 py-2">
|
||||||
>
|
<button
|
||||||
</select>
|
class="inline-flex items-center gap-1 rounded bg-blue-600 px-3 py-1.5 text-white text-xs font-medium hover:bg-blue-700"
|
||||||
</td>
|
on:click={() => openModal(row)}
|
||||||
{:else if col.key === "proses_to_approval"}
|
>
|
||||||
<td class="px-4 py-2">
|
✏️ Edit
|
||||||
<!-- checkbox -->
|
</button>
|
||||||
<input
|
<button
|
||||||
type="checkbox"
|
class="inline-flex items-center gap-1 rounded bg-red-600 px-3 py-1.5 text-white text-xs font-medium hover:bg-red-700"
|
||||||
checked={row.proses_to_approval}
|
on:click={() => deleteProject(row.id)}
|
||||||
on:change={(e) => {
|
>
|
||||||
const isChecked = (
|
🗑️ Delete
|
||||||
e.target as HTMLInputElement
|
</button>
|
||||||
).checked;
|
</td>
|
||||||
|
{:else}
|
||||||
if (isChecked) {
|
<td class="px-4 py-2 text-gray-700">
|
||||||
newPurchaseOrders = {
|
{row[col.key] ?? "—"}
|
||||||
...row,
|
</td>
|
||||||
proses_to_approval: true,
|
{/if}
|
||||||
};
|
{/each}
|
||||||
// map to project
|
</tr>
|
||||||
updateProsesToApproval(
|
|
||||||
row.id,
|
|
||||||
isChecked,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
newPurchaseOrders = {
|
|
||||||
...row,
|
|
||||||
proses_to_approval: false,
|
|
||||||
};
|
|
||||||
// uncheck
|
|
||||||
updateProsesToApproval(
|
|
||||||
row.id,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled={row.proses_to_approval}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
{:else if col.key === "acknowledged"}
|
|
||||||
<td class="px-4 py-2 text-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={row.acknowledged}
|
|
||||||
on:change={async (e) => {
|
|
||||||
const isChecked = (
|
|
||||||
e.target as HTMLInputElement
|
|
||||||
).checked;
|
|
||||||
row.acknowledged = isChecked;
|
|
||||||
|
|
||||||
if (isChecked) {
|
|
||||||
// map to project
|
|
||||||
await acknowledgedOk(
|
|
||||||
row.id,
|
|
||||||
isChecked,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
{:else if col.key === "received"}
|
|
||||||
<td class="px-4 py-2 text-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={row.received}
|
|
||||||
on:change={async (e) => {
|
|
||||||
const isChecked = (
|
|
||||||
e.target as HTMLInputElement
|
|
||||||
).checked;
|
|
||||||
row.received = isChecked;
|
|
||||||
|
|
||||||
if (isChecked) {
|
|
||||||
// map to project
|
|
||||||
await receivedOk(
|
|
||||||
row.id,
|
|
||||||
isChecked,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
{:else if col.key === "completed_status"}
|
|
||||||
<td class="px-4 py-2">
|
|
||||||
<select
|
|
||||||
bind:value={
|
|
||||||
row[
|
|
||||||
col.key as keyof PurchaseOrderDisplay
|
|
||||||
]
|
|
||||||
}
|
|
||||||
class="w-full p-3 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500 text-gray-900"
|
|
||||||
on:change={async (e) => {
|
|
||||||
const isValue = (
|
|
||||||
e.target as HTMLInputElement
|
|
||||||
).value;
|
|
||||||
|
|
||||||
if (isValue) {
|
|
||||||
// map to project
|
|
||||||
await completedStatusOk(
|
|
||||||
row.id,
|
|
||||||
isValue,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<option value="" disabled selected
|
|
||||||
>ON PROSES</option
|
|
||||||
>
|
|
||||||
<option value="RECEIVED COMPLETE"
|
|
||||||
>RECEIVED COMPLETE</option
|
|
||||||
>
|
|
||||||
<option value="RECEIVED INCOMPLETE"
|
|
||||||
>COMPLETE INCOMPLETE</option
|
|
||||||
>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
{:else if col.key === "updated_at"}
|
|
||||||
<td class="px-4 py-2 text-gray-500"
|
|
||||||
>{new Date(
|
|
||||||
row[
|
|
||||||
col.key as keyof PurchaseOrderDisplay
|
|
||||||
] as string,
|
|
||||||
).toLocaleString()}</td
|
|
||||||
>
|
|
||||||
{:else if col.key === "actions"}
|
|
||||||
<td class="px-4 py-2">
|
|
||||||
<button
|
|
||||||
class="inline-flex items-center gap-1 rounded bg-blue-600 px-3 py-1.5 text-white text-xs font-medium hover:bg-blue-700"
|
|
||||||
on:click={() => openModal(row)}
|
|
||||||
>
|
|
||||||
✏️ Edit
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="inline-flex items-center gap-1 rounded bg-red-600 px-3 py-1.5 text-white text-xs font-medium hover:bg-red-700"
|
|
||||||
on:click={() => deleteProject(row.id)}
|
|
||||||
>
|
|
||||||
🗑️ Delete
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
{:else if col.key === "created_at"}
|
|
||||||
<td class="px-4 py-2 text-gray-500"
|
|
||||||
>{new Date(
|
|
||||||
row[
|
|
||||||
col.key as keyof PurchaseOrderDisplay
|
|
||||||
] as string,
|
|
||||||
).toLocaleString()}</td
|
|
||||||
>
|
|
||||||
{:else}
|
|
||||||
<td class="px-4 py-2 text-gray-700"
|
|
||||||
>{row[
|
|
||||||
col.key as keyof PurchaseOrderDisplay
|
|
||||||
]}</td
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</tr>
|
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1165,3 +1248,126 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if showPreparedModal}
|
||||||
|
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50 overflow-y-auto">
|
||||||
|
<div class="min-h-screen flex items-center justify-center p-4">
|
||||||
|
<div class="bg-white rounded-lg shadow-lg w-full max-w-2xl max-h-[90vh] overflow-y-auto p-6 space-y-4">
|
||||||
|
<h2 class="text-lg font-bold">Set Prepared</h2>
|
||||||
|
|
||||||
|
<!-- prepared_by -->
|
||||||
|
<label>Prepared By</label>
|
||||||
|
<select bind:value={preparedForm.prepared_by} class="w-full border p-2">
|
||||||
|
<option value="" disabled>Select Employee</option>
|
||||||
|
{#each preparedByOptions as emp}
|
||||||
|
<option value={emp.id}>{emp.employee_name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- prepared_date -->
|
||||||
|
<label>Prepared Date</label>
|
||||||
|
<input type="date" bind:value={preparedForm.prepared_date} class="w-full border p-2" />
|
||||||
|
|
||||||
|
<!-- po_item -->
|
||||||
|
<label>PO Item</label>
|
||||||
|
<select bind:value={preparedForm.po_item} class="w-full border p-2">
|
||||||
|
<option value="" disabled>Select Item</option>
|
||||||
|
{#each poItemOptions as item}
|
||||||
|
<option value={item.item_name}>{item.item_name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- po_quantity -->
|
||||||
|
<label>PO Quantity</label>
|
||||||
|
<input type="number" bind:value={preparedForm.po_quantity} class="w-full border p-2" />
|
||||||
|
|
||||||
|
<!-- Vendors and prices -->
|
||||||
|
<label>Q1 Vendor</label>
|
||||||
|
<select bind:value={preparedForm.q1_vendor} class="w-full border p-2">
|
||||||
|
<option value="" disabled>Select Vendor</option>
|
||||||
|
{#each vendorOptions as v}
|
||||||
|
<option value={v.name}>{v.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
<input type="number" bind:value={preparedForm.q1_vendor_price} placeholder="Q1 Vendor Price" class="w-full border p-2" />
|
||||||
|
|
||||||
|
<!-- Repeat for Q2, Q3 -->
|
||||||
|
<!-- Approved -->
|
||||||
|
<label>Approved Quantity</label>
|
||||||
|
<input type="number" bind:value={preparedForm.approved_quantity} on:input={() => updateTotalAmount()} class="w-full border p-2" />
|
||||||
|
|
||||||
|
<label>Approved Vendor</label>
|
||||||
|
<select bind:value={preparedForm.approved_vendor} class="w-full border p-2">
|
||||||
|
<option value="" disabled>Select Vendor</option>
|
||||||
|
{#each vendorOptions as v}
|
||||||
|
<option value={v.name}>{v.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label>Approved Price</label>
|
||||||
|
<input type="number" bind:value={preparedForm.approved_price} on:input={() => updateTotalAmount()} class="w-full border p-2" />
|
||||||
|
|
||||||
|
<label>Total Approved Order Amount</label>
|
||||||
|
<input type="number" value={preparedForm.total_approved_order_amount} disabled class="w-full border p-2 bg-gray-100" />
|
||||||
|
|
||||||
|
<label>PO Remark</label>
|
||||||
|
<textarea bind:value={preparedForm.po_remark} class="w-full border p-2"></textarea>
|
||||||
|
|
||||||
|
<div class="flex justify-end space-x-2 pt-4">
|
||||||
|
<button on:click={savePrepared} class="bg-blue-600 text-white px-4 py-2 rounded">Save</button>
|
||||||
|
<button on:click={() => showPreparedModal = false} class="px-4 py-2 rounded border">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if showPaymentModal}
|
||||||
|
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50 overflow-y-auto">
|
||||||
|
<div class="min-h-screen flex items-center justify-center p-4">
|
||||||
|
<div class="bg-white rounded-lg shadow-lg w-full max-w-2xl max-h-[90vh] overflow-y-auto p-6 space-y-4">
|
||||||
|
<h2 class="text-xl font-bold">Set Payment</h2>
|
||||||
|
|
||||||
|
<!-- Payment Method -->
|
||||||
|
<label>Payment Method</label>
|
||||||
|
<select bind:value={paymentForm.payment_method} class="w-full border p-2">
|
||||||
|
<option value="" disabled>Select Method</option>
|
||||||
|
<option value="Gradualy">Gradualy</option>
|
||||||
|
<option value="Full Before Received">Full Before Received</option>
|
||||||
|
<option value="Full After Received">Full After Received</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- Total Approved Order Amount (hidden input, readonly) -->
|
||||||
|
<label>Total Approved Order Amount</label>
|
||||||
|
<input type="number" value={paymentForm.total_approved_order_amount} disabled class="w-full border p-2 bg-gray-100"/>
|
||||||
|
|
||||||
|
<!-- Payment Amounts -->
|
||||||
|
{#each [1,2,3,4,5,6] as num}
|
||||||
|
<label>{ordinal(num)} Pay Amount</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
bind:value={paymentForm[`${ordinal(num)}_pay_amt`]}
|
||||||
|
on:input={updateDueRemaining}
|
||||||
|
class="w-full border p-2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label>{ordinal(num)} Pay Date</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
bind:value={paymentForm[`${ordinal(num)}_pay_date`]}
|
||||||
|
class="w-full border p-2"
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
<!-- Due Remaining -->
|
||||||
|
<label>Due Remaining</label>
|
||||||
|
<input type="number" value={paymentForm.due_remaining} disabled class="w-full border p-2 bg-gray-100"/>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<div class="flex justify-end space-x-2 pt-4">
|
||||||
|
<button on:click={savePayment} class="bg-blue-600 text-white px-4 py-2 rounded">Save</button>
|
||||||
|
<button on:click={() => showPaymentModal = false} class="px-4 py-2 rounded border">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
Reference in New Issue
Block a user