From 2a2cc0fb962e7a91d4e2d3115ec42b89a0d45faa Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Fri, 19 Jun 2026 19:19:50 +0700 Subject: [PATCH] feat: add shipping cost field to purchase management, including validation, calculations, and UI updates for total cost --- .../Requests/Admin/Manage/PurchaseRequest.php | 2 ++ app/Models/Purchase.php | 9 +++++++ app/Services/Manage/PurchaseService.php | 8 ++++-- app/Services/Master/RawMaterialService.php | 1 - database/factories/PurchaseFactory.php | 4 ++- ...26_06_11_120001_create_purchases_table.php | 1 + .../hr/attendances/AttendanceDetailDialog.vue | 4 +-- .../manage/purchases/PurchaseGroupedTable.vue | 2 ++ .../manage/purchases/PurchasePosForm.vue | 12 ++++++++- .../master/raw-materials/RawMaterialForm.vue | 25 +++++++++++++++++-- .../js/pages/admin/manage/purchases/Edit.vue | 1 + resources/js/types/purchase.ts | 3 +++ 12 files changed, 63 insertions(+), 9 deletions(-) diff --git a/app/Http/Requests/Admin/Manage/PurchaseRequest.php b/app/Http/Requests/Admin/Manage/PurchaseRequest.php index dffb99f..03f22a1 100644 --- a/app/Http/Requests/Admin/Manage/PurchaseRequest.php +++ b/app/Http/Requests/Admin/Manage/PurchaseRequest.php @@ -28,6 +28,7 @@ public function rules(): array $rules = [ 'supplier_id' => ['required', 'integer', Rule::exists('suppliers', 'id')->whereNull('deleted_at')], 'discount' => ['nullable', 'integer', 'min:0'], + 'shipping_cost' => ['nullable', 'integer', 'min:0'], 'notes' => ['nullable', 'string', 'max:100'], ...$this->photoRules(), ]; @@ -53,6 +54,7 @@ public function attributes(): array return [ 'supplier_id' => 'supplier', 'discount' => 'diskon', + 'shipping_cost' => 'ongkir', 'notes' => 'keterangan', 'items' => 'bahan baku', 'items.*.raw_material_price_id' => 'bahan baku', diff --git a/app/Models/Purchase.php b/app/Models/Purchase.php index 545a543..32d8c68 100644 --- a/app/Models/Purchase.php +++ b/app/Models/Purchase.php @@ -18,6 +18,7 @@ #[Appends([ 'subtotal_formatted', 'discount_formatted', + 'shipping_cost_formatted', 'total_formatted', 'created_at_formatted', ])] @@ -33,6 +34,7 @@ protected function casts(): array return [ 'subtotal' => 'integer', 'discount' => 'integer', + 'shipping_cost' => 'integer', 'total' => 'integer', ]; } @@ -66,6 +68,13 @@ public function discountFormatted(): Attribute ); } + public function shippingCostFormatted(): Attribute + { + return Attribute::make( + get: fn () => 'Rp '.number_format($this->shipping_cost, 0, ',', '.'), + ); + } + public function subtotalFormatted(): Attribute { return Attribute::make( diff --git a/app/Services/Manage/PurchaseService.php b/app/Services/Manage/PurchaseService.php index 0f0d0c5..dffc5c7 100644 --- a/app/Services/Manage/PurchaseService.php +++ b/app/Services/Manage/PurchaseService.php @@ -223,13 +223,15 @@ public function create(array $validated, User $user): Purchase $subtotal = $draftItems->sum('subtotal'); $discount = (int) ($validated['discount'] ?? 0); - $total = max($subtotal - $discount, 0); + $shippingCost = (int) ($validated['shipping_cost'] ?? 0); + $total = max($subtotal - $discount + $shippingCost, 0); $purchase = Purchase::create([ 'supplier_id' => $validated['supplier_id'], 'created_by_id' => $user->id, 'subtotal' => $subtotal, 'discount' => $discount, + 'shipping_cost' => $shippingCost, 'total' => $total, 'notes' => $validated['notes'] ?? null, ]); @@ -273,11 +275,13 @@ public function update(Purchase $purchase, array $validated): void $lineItems = $this->buildLineItems($validated['items']); $subtotal = array_sum(array_column($lineItems, 'subtotal')); $discount = (int) ($validated['discount'] ?? 0); - $total = max($subtotal - $discount, 0); + $shippingCost = (int) ($validated['shipping_cost'] ?? 0); + $total = max($subtotal - $discount + $shippingCost, 0); $purchase->supplier_id = $validated['supplier_id']; $purchase->subtotal = $subtotal; $purchase->discount = $discount; + $purchase->shipping_cost = $shippingCost; $purchase->total = $total; $purchase->notes = $validated['notes'] ?? null; $purchase->save(); diff --git a/app/Services/Master/RawMaterialService.php b/app/Services/Master/RawMaterialService.php index 2a9d413..b4f67a3 100644 --- a/app/Services/Master/RawMaterialService.php +++ b/app/Services/Master/RawMaterialService.php @@ -191,7 +191,6 @@ public function update(RawMaterial $rawMaterial, array $validated): void { DB::transaction(function () use ($validated, $rawMaterial): void { $rawMaterial->name = $validated['name']; - $rawMaterial->unit = $validated['unit']; $rawMaterial->save(); $submittedPriceIds = collect($validated['prices']) diff --git a/database/factories/PurchaseFactory.php b/database/factories/PurchaseFactory.php index ad3a074..a85dac9 100644 --- a/database/factories/PurchaseFactory.php +++ b/database/factories/PurchaseFactory.php @@ -16,13 +16,15 @@ public function definition(): array { $subtotal = fake()->numberBetween(100_000, 10_000_000); $discount = fake()->numberBetween(0, (int) ($subtotal * 0.1)); + $shippingCost = fake()->numberBetween(0, 100_000); return [ 'supplier_id' => Supplier::factory(), 'created_by_id' => User::factory(), 'subtotal' => $subtotal, 'discount' => $discount, - 'total' => $subtotal - $discount, + 'shipping_cost' => $shippingCost, + 'total' => $subtotal - $discount + $shippingCost, 'notes' => fake()->optional()->sentence(3), ]; } diff --git a/database/migrations/2026_06_11_120001_create_purchases_table.php b/database/migrations/2026_06_11_120001_create_purchases_table.php index 23067cf..8381b3e 100644 --- a/database/migrations/2026_06_11_120001_create_purchases_table.php +++ b/database/migrations/2026_06_11_120001_create_purchases_table.php @@ -16,6 +16,7 @@ public function up(): void $table->unsignedBigInteger('subtotal'); $table->unsignedBigInteger('discount')->default(0); + $table->unsignedBigInteger('shipping_cost')->default(0); $table->unsignedBigInteger('total'); $table->string('notes', 100)->nullable(); diff --git a/resources/js/components/admin/hr/attendances/AttendanceDetailDialog.vue b/resources/js/components/admin/hr/attendances/AttendanceDetailDialog.vue index e7779be..df9ea63 100644 --- a/resources/js/components/admin/hr/attendances/AttendanceDetailDialog.vue +++ b/resources/js/components/admin/hr/attendances/AttendanceDetailDialog.vue @@ -53,7 +53,7 @@ function destroyAttendance() {