浏览代码

fix: sale and purchase master

父节点
当前提交
45394b3b73

+ 2
- 0
app/Http/Controllers/PurchaseController.php 查看文件

@@ -142,6 +142,7 @@ class PurchaseController extends Controller
142 142
             "id" => $purchase->id,
143 143
             "number" => $purchase->number,
144 144
             "ppn" => Ppn::first()->ppn,
145
+            "status" => $purchase->status,
145 146
             "productNumber" => "PDK" . now()->format("YmdHis"),
146 147
             "ppnChecked" => $purchase->ppn ? true : false,
147 148
             "supplier" => $purchase->supplier,
@@ -212,6 +213,7 @@ class PurchaseController extends Controller
212 213
                 if ($request->status == "success") {
213 214
                     $validated = [
214 215
                         "purchase_number" => $purchase->number,
216
+                        "price" => $product["price"],
215 217
                         "qty" => $product["qty"],
216 218
                         "product_number" => $product["number"],
217 219
                     ];

+ 114
- 34
app/Http/Controllers/SalesController.php 查看文件

@@ -2,15 +2,17 @@
2 2
 
3 3
 namespace App\Http\Controllers;
4 4
 
5
-use App\Http\Requests\Sales\StoreSaleRequest;
6
-use App\Http\Requests\Sales\UpdateSaleRequest;
7
-use App\Models\Customer;
8 5
 use App\Models\Ppn;
9 6
 use App\Models\Sale;
7
+use Inertia\Inertia;
8
+use App\Models\Customer;
9
+use App\Models\SaleDetail;
10 10
 use App\Models\StockProduct;
11
-use Illuminate\Database\QueryException;
11
+use App\Services\HelperService;
12 12
 use Illuminate\Support\Facades\DB;
13
-use Inertia\Inertia;
13
+use Illuminate\Database\QueryException;
14
+use App\Http\Requests\Sales\StoreSaleRequest;
15
+use App\Http\Requests\Sales\UpdateSaleRequest;
14 16
 
15 17
 class SalesController extends Controller
16 18
 {
@@ -36,13 +38,13 @@ class SalesController extends Controller
36 38
                     fn($sale) => [
37 39
                         "id" => $sale->id,
38 40
                         "updatedAt" => $sale->updated_at,
39
-                        "number" => $sale->number,
41
+                        "name" => $sale->customer->name,
42
+                        "phone" => $sale->customer->phone,
43
+                        "email" => $sale->customer->email,
44
+                        "price" => HelperService::setRupiahFormat(
45
+                            SaleDetail::totalPrice($sale)
46
+                        ),
40 47
                         "status" => $sale->status,
41
-                        "price" => $sale->saleDetail->price,
42
-                        "ppn" => $sale->saleDetail->ppn,
43
-                        "qty" => $sale->saleDetail->qty,
44
-                        "productName" => $sale->product->name,
45
-                        "productNumber" => $sale->product->number,
46 48
                     ]
47 49
                 ),
48 50
         ]);
@@ -58,9 +60,6 @@ class SalesController extends Controller
58 60
         return inertia("Sales/Create", [
59 61
             "number" => "PJN" . now()->format("YmdHis"),
60 62
             "ppn" => Ppn::first()->ppn,
61
-            "productNumber" => Inertia::lazy(
62
-                fn() => "PDK" . now()->format("YmdHis")
63
-            ),
64 63
             "customers" => Inertia::lazy(
65 64
                 fn() => Customer::filter([
66 65
                     "search" => request("customer"),
@@ -75,6 +74,7 @@ class SalesController extends Controller
75 74
                         fn($stockProduct) => [
76 75
                             "number" => $stockProduct->product_number,
77 76
                             "name" => $stockProduct->product->name,
77
+                            "price" => $stockProduct->price,
78 78
                             "qty" => $stockProduct->qty,
79 79
                             "unit" => $stockProduct->product->unit,
80 80
                         ]
@@ -91,20 +91,43 @@ class SalesController extends Controller
91 91
      */
92 92
     public function store(StoreSaleRequest $request)
93 93
     {
94
+        dd($request->validated());
95
+
94 96
         DB::beginTransaction();
95 97
 
96 98
         try {
99
+            $ppn = Ppn::first()->ppn;
100
+
97 101
             $validated = $request
98 102
                 ->safe()
99 103
                 ->merge([
100 104
                     "user_id" => auth()->user()->id,
101
-                    "ppn" => Ppn::first()->ppn,
105
+                    "ppn" => $request->ppn ? $ppn : 0,
102 106
                 ])
103 107
                 ->all();
104 108
 
105 109
             $sale = Sale::create($validated);
106 110
 
107
-            $sale->saleDetail()->create($validated);
111
+            foreach ($request->products as $product) {
112
+                $validated = [
113
+                    "product_number" => $product["number"],
114
+                    "price" => $product["price"],
115
+                    "qty" => $product["qty"],
116
+                ];
117
+
118
+                $sale->saleDetail()->create($validated);
119
+
120
+                if ($request->status == "success") {
121
+                    $validated = [
122
+                        "sale_number" => $sale->number,
123
+                        "price" => $product["price"],
124
+                        "qty" => $product["qty"],
125
+                        "product_number" => $product["number"],
126
+                    ];
127
+
128
+                    StockProduct::create($validated);
129
+                }
130
+            }
108 131
 
109 132
             DB::commit();
110 133
 
@@ -136,16 +159,38 @@ class SalesController extends Controller
136 159
     public function edit(Sale $sale)
137 160
     {
138 161
         return inertia("Sales/Edit", [
139
-            "sale" => [
140
-                "id" => $sale->id,
141
-                "number" => $sale->number,
142
-                "status" => $sale->status,
143
-                "price" => $sale->saleDetail->price,
144
-                "qty" => $sale->saleDetail->qty,
145
-                "ppn" => $sale->saleDetail->ppn,
146
-                "customer" => $sale->customer,
147
-                "product" => $sale->product,
148
-            ],
162
+            "id" => $sale->id,
163
+            "number" => $sale->number,
164
+            "ppn" => Ppn::first()->ppn,
165
+            "status" => $sale->status,
166
+            "ppnChecked" => $sale->ppn ? true : false,
167
+            "customer" => $sale->customer,
168
+            "stockProducts" => Inertia::lazy(
169
+                fn() => StockProduct::filter([
170
+                    "search" => request("stockProduct"),
171
+                ])
172
+                    ->get()
173
+                    ->transform(
174
+                        fn($stockProduct) => [
175
+                            "number" => $stockProduct->product_number,
176
+                            "name" => $stockProduct->product->name,
177
+                            "price" => $stockProduct->price,
178
+                            "qty" => $stockProduct->qty,
179
+                            "unit" => $stockProduct->product->unit,
180
+                            "profit" => $stockProduct->product->profit,
181
+                        ]
182
+                    )
183
+            ),
184
+            "saleDetail" => $sale->saleDetail->transform(
185
+                fn($sale) => [
186
+                    "id" => $sale->id,
187
+                    "number" => $sale->product_number,
188
+                    "name" => $sale->product->name,
189
+                    "price" => $sale->price,
190
+                    "qty" => $sale->qty,
191
+                    "unit" => $sale->product->unit,
192
+                ]
193
+            ),
149 194
         ]);
150 195
     }
151 196
 
@@ -159,19 +204,54 @@ class SalesController extends Controller
159 204
     public function update(UpdateSaleRequest $request, Sale $sale)
160 205
     {
161 206
         dd($sale);
207
+
162 208
         DB::beginTransaction();
163 209
 
164 210
         try {
165
-            $sale->update($request->validated());
211
+            $ppn = Ppn::first()->ppn;
212
+
213
+            $validated = $request
214
+                ->safe()
215
+                ->merge([
216
+                    "ppn" => $request->ppn ? $ppn : 0,
217
+                ])
218
+                ->all();
219
+
220
+            $sale->update($validated);
221
+
222
+            foreach ($request->products as $product) {
223
+                $validated = [
224
+                    "product_number" => $product["number"],
225
+                    "price" => $product["price"],
226
+                    "qty" => $product["qty"],
227
+                ];
228
+
229
+                if (!empty($product["label"])) {
230
+                    if ($product["label"] == "add") {
231
+                        $sale->saleDetail()->create($validated);
232
+                    }
233
+
234
+                    if ($product["label"] == "edit") {
235
+                        $sale->saleDetail
236
+                            ->find($product["id"])
237
+                            ->update($validated);
238
+                    }
239
+
240
+                    if ($product["label"] == "delete") {
241
+                        $sale->saleDetail->find($product["id"])->delete();
242
+                    }
243
+                }
166 244
 
167
-            $sale->saleDetail()->update($request->safe()->except("status"));
245
+                if ($request->status == "success") {
246
+                    $validated = [
247
+                        "sale_number" => $sale->number,
248
+                        "price" => $product["price"],
249
+                        "qty" => $product["qty"],
250
+                        "product_number" => $product["number"],
251
+                    ];
168 252
 
169
-            if ($request->status == "success") {
170
-                StockProduct::create([
171
-                    "sale_number" => $sale->number,
172
-                    "qty" => -$request->qty,
173
-                    "product_number" => $sale->saleDetail->product_number,
174
-                ]);
253
+                    StockProduct::create($validated);
254
+                }
175 255
             }
176 256
 
177 257
             DB::commit();

+ 1
- 1
app/Http/Requests/Purchase/StorePurchaseRequest.php 查看文件

@@ -26,9 +26,9 @@ class StorePurchaseRequest extends FormRequest
26 26
         return [
27 27
             "number" => "required|string|unique:sales,number",
28 28
             "status" => "required|string",
29
+            "ppn" => "required|boolean",
29 30
             "supplier_id" => "required|numeric",
30 31
             "products" => "required",
31
-            "ppn" => "required|boolean",
32 32
         ];
33 33
     }
34 34
 }

+ 1
- 1
app/Http/Requests/Sales/StoreSaleRequest.php 查看文件

@@ -26,9 +26,9 @@ class StoreSaleRequest extends FormRequest
26 26
         return [
27 27
             "number" => "required|string|unique:sales,number",
28 28
             "status" => "required|string",
29
+            "ppn" => "required|boolean",
29 30
             "customer_id" => "required|numeric",
30 31
             "products" => "required",
31
-            "ppn" => "required|boolean",
32 32
         ];
33 33
     }
34 34
 }

+ 2
- 2
app/Http/Requests/Sales/UpdateSaleRequest.php 查看文件

@@ -25,8 +25,8 @@ class UpdateSaleRequest extends FormRequest
25 25
     {
26 26
         return [
27 27
             "status" => "required|string",
28
-            "price" => "required|numeric",
29
-            "qty" => "required|numeric",
28
+            "products" => "required",
29
+            "ppn" => "required|boolean",
30 30
         ];
31 31
     }
32 32
 }

+ 1
- 1
app/Models/Sale.php 查看文件

@@ -24,7 +24,7 @@ class Sale extends Model
24 24
 
25 25
     public function saleDetail()
26 26
     {
27
-        return $this->hasOne(SaleDetail::class, "sale_number", "number");
27
+        return $this->hasMany(SaleDetail::class, "sale_number", "number");
28 28
     }
29 29
 
30 30
     public function customer()

+ 12
- 0
app/Models/StockProduct.php 查看文件

@@ -2,6 +2,8 @@
2 2
 
3 3
 namespace App\Models;
4 4
 
5
+use App\Services\HelperService;
6
+use Illuminate\Database\Eloquent\Casts\Attribute;
5 7
 use Illuminate\Database\Eloquent\Factories\HasFactory;
6 8
 use Illuminate\Database\Eloquent\Model;
7 9
 
@@ -12,10 +14,20 @@ class StockProduct extends Model
12 14
     protected $fillable = [
13 15
         "purchase_number",
14 16
         "sale_number",
17
+        "price",
15 18
         "qty",
16 19
         "product_number",
17 20
     ];
18 21
 
22
+    protected function price(): Attribute
23
+    {
24
+        $ppn = Purchase::where("number", $this->purchase_number)->first()->ppn;
25
+
26
+        return Attribute::make(
27
+            get: fn($value) => HelperService::addPPN($value, $ppn)
28
+        );
29
+    }
30
+
19 31
     public function product()
20 32
     {
21 33
         return $this->belongsTo(Product::class, "product_number", "number");

+ 5
- 0
app/Services/HelperService.php 查看文件

@@ -35,4 +35,9 @@ class HelperService
35 35
             }
36 36
         }
37 37
     }
38
+
39
+    public static function addPPN(int $price, int $ppn)
40
+    {
41
+        return $price + $price * ($ppn / 100);
42
+    }
38 43
 }

+ 1
- 1
app/Services/PurchaseService.php 查看文件

@@ -14,7 +14,7 @@ class PurchaseService
14 14
             $ppn = Ppn::first()->ppn;
15 15
 
16 16
             return $purchase->ppn
17
-                ? $purchaseDetail->price + $purchaseDetail->price * ($ppn / 100)
17
+                ? HelperService::addPPN($purchaseDetail->price, $ppn)
18 18
                 : $purchaseDetail->price;
19 19
         });
20 20
     }

+ 21
- 0
app/Services/SaleService.php 查看文件

@@ -0,0 +1,21 @@
1
+<?php
2
+
3
+namespace App\Services;
4
+
5
+use App\Models\Ppn;
6
+use App\Models\Sale;
7
+use App\Services\HelperService;
8
+
9
+class SaleService
10
+{
11
+    public static function totalPrice(Sale $sale)
12
+    {
13
+        return $sale->saleDetail->sum(function ($saleDetail) use ($sale) {
14
+            $ppn = Ppn::first()->ppn;
15
+
16
+            return $sale->ppn
17
+                ? HelperService::addPPN($saleDetail->price, $ppn)
18
+                : $saleDetail->price;
19
+        });
20
+    }
21
+}

+ 1
- 0
database/migrations/2022_06_16_092657_create_stock_products_table.php 查看文件

@@ -22,6 +22,7 @@ return new class extends Migration {
22 22
                 ->string("sale_number")
23 23
                 ->nullable()
24 24
                 ->default(null);
25
+            $table->unsignedInteger("price");
25 26
             $table->integer("qty");
26 27
             $table->string("product_number");
27 28
             $table

+ 1
- 0
resources/js/pages/Purchases/Components/Details.vue 查看文件

@@ -61,6 +61,7 @@ defineProps({
61 61
         </div>
62 62
 
63 63
         <Divider type="dashed" />
64
+
64 65
         <div class="col-12">
65 66
           <div class="grid">
66 67
             <div v-if="price" class="col">

+ 9
- 2
resources/js/pages/Purchases/Composables/useProductCart.js 查看文件

@@ -48,10 +48,16 @@ export function useProductCart(form, initialProducts = []) {
48 48
     productCart.splice(index, 1)
49 49
   }
50 50
 
51
-  const onClearProduct = () => {
51
+  const onClearProductCart = () => {
52 52
     productCart.splice(0)
53 53
   }
54 54
 
55
+  const onClearProductCartDelete = () => {
56
+    productCartDeleted.splice(0)
57
+
58
+    console.info('this is running')
59
+  }
60
+
55 61
   const totalProductPrice = () => {
56 62
     const productPrices = productCart.map((product) => {
57 63
       if (form.checkedPpn) {
@@ -76,10 +82,11 @@ export function useProductCart(form, initialProducts = []) {
76 82
   return {
77 83
     productCart,
78 84
     productCartDeleted,
85
+    onClearProductCart,
86
+    onClearProductCartDelete,
79 87
     onAddProduct,
80 88
     onDeleteProduct,
81 89
     onEditProduct,
82
-    onClearProduct,
83 90
     totalProductPrice,
84 91
   }
85 92
 }

+ 10
- 5
resources/js/pages/Purchases/Create.vue 查看文件

@@ -11,6 +11,7 @@ import AppInputNumber from '@/components/AppInputNumber.vue'
11 11
 import AppDropdown from '@/components/AppDropdown.vue'
12 12
 import AppAutoComplete from '@/components/AppAutoComplete.vue'
13 13
 import DashboardLayout from '@/layouts/Dashboard/DashboardLayout.vue'
14
+import { computed } from '@vue/reactivity'
14 15
 
15 16
 const props = defineProps({
16 17
   number: String,
@@ -53,6 +54,10 @@ const onSubmit = () => {
53 54
     })
54 55
 }
55 56
 
57
+const dropdownStatus = computed(() => {
58
+  return optionStatus.filter((val) => val.value != 'success')
59
+})
60
+
56 61
 const {
57 62
   productCart,
58 63
   onAddProduct,
@@ -74,14 +79,14 @@ const { onShowCreateProduct, onShowCreateSupplier } = onShowDialog()
74 79
         <div class="grid">
75 80
           <div class="col-12">
76 81
             <Card>
77
-              <template #title> Pembeli </template>
82
+              <template #title> Penjual </template>
78 83
               <template #content>
79 84
                 <div class="grid">
80 85
                   <div class="col-12 md:col-6">
81 86
                     <AppDropdown
82 87
                       label="Status"
83 88
                       placeholder="status"
84
-                      :options="optionStatus"
89
+                      :options="dropdownStatus"
85 90
                       :error="form.errors.status"
86 91
                       v-model="form.status"
87 92
                     />
@@ -102,7 +107,7 @@ const { onShowCreateProduct, onShowCreateSupplier } = onShowDialog()
102 107
                         <template v-if="slotProps.item">
103 108
                           <div class="flex flex-column">
104 109
                             <span>{{ slotProps.item.name }}</span>
105
-                            <span>{{ slotProps.item.npwp }}</span>
110
+                            <span>{{ slotProps.item.phone }}</span>
106 111
                           </div>
107 112
                         </template>
108 113
                       </template>
@@ -137,7 +142,7 @@ const { onShowCreateProduct, onShowCreateSupplier } = onShowDialog()
137 142
                       field="name"
138 143
                       refresh-data="products"
139 144
                       v-model="form.product"
140
-                      :error="form.errors.product"
145
+                      :error="form.errors.products"
141 146
                       :suggestions="products"
142 147
                     >
143 148
                       <template #item="slotProps">
@@ -161,7 +166,7 @@ const { onShowCreateProduct, onShowCreateSupplier } = onShowDialog()
161 166
                     </AppAutoComplete>
162 167
                   </div>
163 168
 
164
-                  <div v-if="form.product?.unit" class="col-12 md:col-6">
169
+                  <div v-if="form.product?.number" class="col-12 md:col-6">
165 170
                     <AppInputText
166 171
                       disabled
167 172
                       label="Satuan"

+ 9
- 5
resources/js/pages/Purchases/Edit.vue 查看文件

@@ -16,6 +16,7 @@ const props = defineProps({
16 16
   id: Number,
17 17
   number: String,
18 18
   ppn: Number,
19
+  status: String,
19 20
   ppnChecked: Boolean,
20 21
   supplier: Object,
21 22
   products: {
@@ -26,7 +27,7 @@ const props = defineProps({
26 27
 })
27 28
 
28 29
 const form = useForm({
29
-  status: 'pending',
30
+  status: props.status,
30 31
   price: null,
31 32
   qty: null,
32 33
   supplier: props.supplier,
@@ -42,12 +43,15 @@ const onSubmit = () => {
42 43
       ppn: data.checkedPpn,
43 44
       products: [...productCart, ...productCartDeleted],
44 45
     }))
45
-    .put(route('purchases.update', props.id))
46
+    .put(route('purchases.update', props.id), {
47
+      onSuccess: () => onClearProductCartDelete(),
48
+    })
46 49
 }
47 50
 
48 51
 const {
49 52
   productCart,
50 53
   productCartDeleted,
54
+  onClearProductCartDelete,
51 55
   onAddProduct,
52 56
   onDeleteProduct,
53 57
   onEditProduct,
@@ -66,7 +70,7 @@ const { onShowCreateProduct } = onShowDialog()
66 70
         <div class="grid">
67 71
           <div class="col-12">
68 72
             <Card>
69
-              <template #title> Pembeli </template>
73
+              <template #title> Penjual </template>
70 74
               <template #content>
71 75
                 <div class="grid">
72 76
                   <div class="col-12 md:col-6">
@@ -106,7 +110,7 @@ const { onShowCreateProduct } = onShowDialog()
106 110
                       field="name"
107 111
                       refresh-data="products"
108 112
                       v-model="form.product"
109
-                      :error="form.errors.product"
113
+                      :error="form.errors.products"
110 114
                       :suggestions="products"
111 115
                     >
112 116
                       <template #item="slotProps">
@@ -130,7 +134,7 @@ const { onShowCreateProduct } = onShowDialog()
130 134
                     </AppAutoComplete>
131 135
                   </div>
132 136
 
133
-                  <div v-if="form.product?.unit" class="col-12 md:col-6">
137
+                  <div v-if="form.product?.number" class="col-12 md:col-6">
134 138
                     <AppInputText
135 139
                       disabled
136 140
                       label="Satuan"

+ 1
- 1
resources/js/pages/Purchases/Index.vue 查看文件

@@ -58,7 +58,7 @@ defineProps({
58 58
           <AppButtonLink
59 59
             icon="pi pi-pencil"
60 60
             class="p-button-icon-only p-button-rounded p-button-text"
61
-            v-tooltip.bottom="'Ubah Penjualan'"
61
+            v-tooltip.bottom="'Ubah Pembelian'"
62 62
             :href="route('purchases.edit', data.id)"
63 63
           />
64 64
         </template>

+ 32
- 8
resources/js/pages/Sales/Components/Cart.vue 查看文件

@@ -1,38 +1,43 @@
1 1
 <script setup>
2
+import { ref } from 'vue'
2 3
 import { IDRCurrencyFormat } from '@/utils/currencyFormat'
4
+import AppInputNumber from '@/components/AppInputNumber.vue'
5
+import AppInputText from '@/components/AppInputText.vue'
3 6
 
4 7
 defineProps({
5 8
   title: String,
6
-  ppn: {
7
-    required: true,
8
-    type: Number,
9
-  },
10 9
   headerTable: {
11 10
     required: true,
12 11
     type: Array,
13 12
   },
14
-  valueTable: {
13
+  productCart: {
15 14
     required: true,
16 15
     type: Array,
17 16
   },
18 17
   checkedPpn: Boolean,
19 18
 })
19
+
20
+const editingRows = ref([])
20 21
 </script>
21 22
 
22 23
 <template>
23 24
   <DataTable
24 25
     responsiveLayout="scroll"
25 26
     columnResizeMode="expand"
26
-    :value="valueTable"
27
+    edit-mode="row"
28
+    data-key="number"
29
+    :value="productCart"
27 30
     :rowHover="true"
28 31
     :stripedRows="true"
32
+    v-model:editing-rows="editingRows"
33
+    @row-edit-save="$emit('edit', $event)"
29 34
   >
30 35
     <template #header>
31 36
       <h2 class="text-2xl font-bold">{{ title }}</h2>
32 37
 
33 38
       <div class="field-checkbox flex justify-content-end gap-2">
34 39
         <label class="text-sm" for="ppn">
35
-          Semua produk dikenakan PPN {{ ppn }}%
40
+          Semua produk dikenakan PPN {{ $page.props.ppn }}%
36 41
         </label>
37 42
         <input
38 43
           type="checkbox"
@@ -45,9 +50,9 @@ defineProps({
45 50
 
46 51
     <Column
47 52
       v-for="value in headerTable"
53
+      :key="value.field"
48 54
       :field="value.field"
49 55
       :header="value.header"
50
-      :key="value.field"
51 56
     >
52 57
       <template #body="{ data, field }">
53 58
         <template v-if="field == 'price'">
@@ -56,8 +61,27 @@ defineProps({
56 61
 
57 62
         <template v-else> {{ data[field] }} </template>
58 63
       </template>
64
+
65
+      <template #editor="{ data, field }">
66
+        <AppInputNumber
67
+          v-if="field == 'price'"
68
+          label="Harga"
69
+          placeholder="harga"
70
+          v-model="data[field]"
71
+        />
72
+
73
+        <AppInputText
74
+          v-if="field == 'qty'"
75
+          label="Kuantitas"
76
+          placeholder="kuantitas"
77
+          type="number"
78
+          v-model="data[field]"
79
+        />
80
+      </template>
59 81
     </Column>
60 82
 
83
+    <Column :row-editor="true" />
84
+
61 85
     <Column>
62 86
       <template #body="{ index }">
63 87
         <Button

+ 4
- 3
resources/js/pages/Sales/Components/Details.vue 查看文件

@@ -23,12 +23,12 @@ defineProps({
23 23
         <div class="col-12">
24 24
           <div class="grid">
25 25
             <div class="col">
26
-              <h3 class="text-base">Nomor Penjualan</h3>
26
+              <h3 class="text-base">Nomor Pembelian</h3>
27 27
               <span>{{ number }}</span>
28 28
             </div>
29 29
 
30 30
             <div class="col">
31
-              <h3 class="text-base">Status Penjualan</h3>
31
+              <h3 class="text-base">Status Pembelian</h3>
32 32
               <span>{{ status }}</span>
33 33
             </div>
34 34
 
@@ -61,10 +61,11 @@ defineProps({
61 61
         </div>
62 62
 
63 63
         <Divider type="dashed" />
64
+
64 65
         <div class="col-12">
65 66
           <div class="grid">
66 67
             <div v-if="price" class="col">
67
-              <h3 class="text-base">Harga</h3>
68
+              <h3 class="text-base">Total Harga</h3>
68 69
               <span>{{ IDRCurrencyFormat(price) }}</span>
69 70
             </div>
70 71
           </div>

+ 24
- 3
resources/js/pages/Sales/Composables/useProductCart.js 查看文件

@@ -1,8 +1,10 @@
1
-import FormValidationError from '@/utils/FormValidationError'
2 1
 import { reactive } from 'vue'
2
+import FormValidationError from '@/utils/FormValidationError'
3
+
4
+export function useProductCart(form, initialProducts = []) {
5
+  const productCart = reactive(initialProducts)
3 6
 
4
-export function useProductCart(form) {
5
-  const productCart = reactive([])
7
+  const productCartDeleted = reactive([])
6 8
 
7 9
   const productValidation = () => {
8 10
     const existProduct = productCart.find(
@@ -21,6 +23,7 @@ export function useProductCart(form) {
21 23
       productValidation()
22 24
 
23 25
       productCart.push({
26
+        label: 'add',
24 27
         number: form.product.number,
25 28
         name: form.product.name,
26 29
         price: form.price,
@@ -35,6 +38,13 @@ export function useProductCart(form) {
35 38
   }
36 39
 
37 40
   const onDeleteProduct = (index) => {
41
+    if (productCart[index]?.id) {
42
+      productCartDeleted.push({
43
+        ...productCart[index],
44
+        label: 'delete',
45
+      })
46
+    }
47
+
38 48
     productCart.splice(index, 1)
39 49
   }
40 50
 
@@ -54,10 +64,21 @@ export function useProductCart(form) {
54 64
     return productPrices.reduce((prev, current) => prev + current, 0)
55 65
   }
56 66
 
67
+  const onEditProduct = (event) => {
68
+    const { newData, index } = event
69
+
70
+    productCart[index] = {
71
+      ...newData,
72
+      label: 'edit',
73
+    }
74
+  }
75
+
57 76
   return {
58 77
     productCart,
78
+    productCartDeleted,
59 79
     onAddProduct,
60 80
     onDeleteProduct,
81
+    onEditProduct,
61 82
     onClearProduct,
62 83
     totalProductPrice,
63 84
   }

+ 45
- 30
resources/js/pages/Sales/Create.vue 查看文件

@@ -1,5 +1,4 @@
1 1
 <script setup>
2
-import { Inertia } from '@inertiajs/inertia'
3 2
 import { optionStatus } from './config'
4 3
 import { cartTable } from './config'
5 4
 import Details from './Components/Details.vue'
@@ -45,13 +44,11 @@ const onSubmit = () => {
45 44
       customer_id: data.customer.id,
46 45
       products: productCart,
47 46
     }))
48
-    .post(route('purchases.store'), {
47
+    .post(route('sales.store'), {
49 48
       onSuccess: () => {
50 49
         form.reset()
51 50
 
52 51
         onClearProduct()
53
-
54
-        Inertia.reload({ only: ['number'] })
55 52
       },
56 53
     })
57 54
 }
@@ -60,6 +57,7 @@ const {
60 57
   productCart,
61 58
   onAddProduct,
62 59
   onDeleteProduct,
60
+  onEditProduct,
63 61
   onClearProduct,
64 62
   totalProductPrice,
65 63
 } = useProductCart(form)
@@ -76,7 +74,7 @@ const { onShowCustomerCreate } = onShowDialog()
76 74
         <div class="grid">
77 75
           <div class="col-12">
78 76
             <Card>
79
-              <template #title> Penjual </template>
77
+              <template #title> Pembeli </template>
80 78
               <template #content>
81 79
                 <div class="grid">
82 80
                   <div class="col-12 md:col-6">
@@ -104,7 +102,7 @@ const { onShowCustomerCreate } = onShowDialog()
104 102
                         <template v-if="slotProps.item">
105 103
                           <div class="flex flex-column">
106 104
                             <span>{{ slotProps.item.name }}</span>
107
-                            <span>{{ slotProps.item.npwp }}</span>
105
+                            <span>{{ slotProps.item.phone }}</span>
108 106
                           </div>
109 107
                         </template>
110 108
                       </template>
@@ -139,7 +137,7 @@ const { onShowCustomerCreate } = onShowDialog()
139 137
                       field="name"
140 138
                       refresh-data="stockProducts"
141 139
                       v-model="form.product"
142
-                      :error="form.errors.product"
140
+                      :error="form.errors.products"
143 141
                       :suggestions="stockProducts"
144 142
                     >
145 143
                       <template #item="slotProps">
@@ -150,35 +148,52 @@ const { onShowCustomerCreate } = onShowDialog()
150 148
                           </div>
151 149
                         </template>
152 150
                       </template>
153
-
154
-                      <template #empty>
155
-                        <span
156
-                          class="cursor-pointer"
157
-                          style="color: var(--primary-color)"
158
-                          @click="onShowCreateProduct"
159
-                        >
160
-                          Tambah Produk
161
-                        </span>
162
-                      </template>
163 151
                     </AppAutoComplete>
164 152
                   </div>
165 153
 
166
-                  <div v-if="form.product?.unit" class="col-12 md:col-6">
167
-                    <AppInputText
168
-                      disabled
169
-                      label="Satuan"
170
-                      placeholder="satuan"
171
-                      v-model="form.product.unit"
172
-                    />
173
-                  </div>
154
+                  <template v-if="form.product?.number">
155
+                    <div class="col-12 md:col-6">
156
+                      <AppInputText
157
+                        disabled
158
+                        label="Satuan"
159
+                        placeholder="satuan"
160
+                        v-model="form.product.unit"
161
+                      />
162
+                    </div>
163
+
164
+                    <div class="col-12 md:col-6">
165
+                      <AppInputNumber
166
+                        disabled
167
+                        label="Harga Beli"
168
+                        placeholder="harga beli produk"
169
+                        v-model="form.product.price"
170
+                      />
171
+                    </div>
172
+
173
+                    <div class="col-12 md:col-6">
174
+                      <AppInputText
175
+                        disabled
176
+                        label="Stok Tersedia"
177
+                        placeholder="stok tersedia"
178
+                        type="number"
179
+                        v-model="form.product.qty"
180
+                      />
181
+                    </div>
182
+                  </template>
183
+
184
+                  <Divider type="dashed" />
174 185
 
175 186
                   <div class="col-12 md:col-6">
176 187
                     <AppInputNumber
188
+                      class="m-0"
177 189
                       :disabled="!form.customer?.id"
178
-                      label="Harga"
179
-                      placeholder="harga"
190
+                      label="Harga Jual"
191
+                      placeholder="harga jual"
180 192
                       v-model="form.price"
181 193
                     />
194
+                    <span v-if="form.product?.number" class="text-xs">
195
+                      Sudah termasuk profit 30%
196
+                    </span>
182 197
                   </div>
183 198
 
184 199
                   <div class="col-12 md:col-6">
@@ -211,11 +226,11 @@ const { onShowCustomerCreate } = onShowDialog()
211 226
           <div class="col-12">
212 227
             <Cart
213 228
               title="Keranjang Produk"
214
-              :ppn="ppn"
215
-              :value-table="productCart"
229
+              :product-cart="productCart"
216 230
               :header-table="cartTable"
217 231
               v-model:checked-ppn="form.checkedPpn"
218 232
               @delete="onDeleteProduct"
233
+              @edit="onEditProduct"
219 234
             />
220 235
           </div>
221 236
         </div>
@@ -223,7 +238,7 @@ const { onShowCustomerCreate } = onShowDialog()
223 238
 
224 239
       <div class="col-12 md:col-4">
225 240
         <Details
226
-          title="Detail Pembelian"
241
+          title="Detail Penjualan"
227 242
           message="Pastikan semua produk sudah benar"
228 243
           :number="number"
229 244
           :status="form.status"

+ 198
- 64
resources/js/pages/Sales/Edit.vue 查看文件

@@ -1,90 +1,224 @@
1 1
 <script setup>
2
-import { useForm } from '@/composables/useForm'
3 2
 import { optionStatus } from './config'
4
-import AppDropdown from '@/components/AppDropdown.vue'
5
-import AppInputNumber from '@/components/AppInputNumber.vue'
6
-import AppInputText from '@/components/AppInputText.vue'
3
+import { cartTable } from './config'
7 4
 import Details from './Components/Details.vue'
5
+import Cart from './Components/Cart.vue'
6
+import { useProductCart } from './Composables/useProductCart'
7
+import { useForm } from '@/composables/useForm'
8
+import AppInputText from '@/components/AppInputText.vue'
9
+import AppInputNumber from '@/components/AppInputNumber.vue'
10
+import AppDropdown from '@/components/AppDropdown.vue'
11
+import AppAutoComplete from '@/components/AppAutoComplete.vue'
8 12
 import DashboardLayout from '@/layouts/Dashboard/DashboardLayout.vue'
9 13
 
10 14
 const props = defineProps({
11
-  sale: Object,
15
+  id: Number,
16
+  number: String,
17
+  ppn: Number,
18
+  status: String,
19
+  ppnChecked: Boolean,
20
+  customer: Object,
21
+  stockProducts: {
22
+    type: Array,
23
+    default: [],
24
+  },
25
+  saleDetail: Array,
12 26
 })
13 27
 
14 28
 const form = useForm({
15
-  status: props.sale.status,
16
-  price: props.sale.price,
17
-  qty: props.sale.qty,
29
+  status: props.status,
30
+  price: null,
31
+  qty: null,
32
+  customer: props.customer,
33
+  product: null,
34
+  ppn: props.ppn,
35
+  checkedPpn: props.ppnChecked,
18 36
 })
19 37
 
20 38
 const onSubmit = () => {
21
-  form.put(route('sales.update', props.sale.id))
39
+  form
40
+    .transform((data) => ({
41
+      status: data.status,
42
+      ppn: data.checkedPpn,
43
+      products: [...productCart, ...productCartDeleted],
44
+    }))
45
+    .post(route('sales.store'), {
46
+      onSuccess: () => {
47
+        form.reset()
48
+
49
+        onClearProduct()
50
+      },
51
+    })
22 52
 }
53
+
54
+const {
55
+  productCart,
56
+  productCartDeleted,
57
+  onAddProduct,
58
+  onDeleteProduct,
59
+  onEditProduct,
60
+  totalProductPrice,
61
+} = useProductCart(form, props.saleDetail)
23 62
 </script>
24 63
 
25 64
 <template>
26 65
   <DashboardLayout title="Ubah Penjualan">
66
+    <DynamicDialog />
67
+
27 68
     <div class="grid">
28
-      <div class="col-12 lg:col-8">
29
-        <Card>
30
-          <template #title> Ubah Penjualan </template>
31
-          <template #content>
32
-            <div class="grid">
33
-              <div class="col-12 md:col-6">
34
-                <AppDropdown
35
-                  label="Status"
36
-                  placeholder="status"
37
-                  :options="optionStatus"
38
-                  :error="form.errors.status"
39
-                  v-model="form.status"
40
-                />
41
-              </div>
42
-
43
-              <div class="col-12 md:col-6">
44
-                <AppInputNumber
45
-                  label="Harga"
46
-                  placeholder="harga"
47
-                  :error="form.errors.price"
48
-                  v-model="form.price"
49
-                />
50
-              </div>
51
-
52
-              <div class="col-12 md:col-6">
53
-                <AppInputText
54
-                  label="Kuantitas"
55
-                  placeholder="kuantitas"
56
-                  type="number"
57
-                  :error="form.errors.qty"
58
-                  v-model="form.qty"
59
-                />
60
-              </div>
61
-            </div>
62
-          </template>
63
-
64
-          <template #footer>
65
-            <div class="flex flex-column md:flex-row justify-content-end">
66
-              <Button
67
-                label="Simpan"
68
-                icon="pi pi-check"
69
-                class="p-button-outlined"
70
-                :disabled="form.processing"
71
-                @click="onSubmit"
72
-              />
73
-            </div>
74
-          </template>
75
-        </Card>
69
+      <div class="col-12 md:col-8">
70
+        <div class="grid">
71
+          <div class="col-12">
72
+            <Card>
73
+              <template #title> Pembeli </template>
74
+              <template #content>
75
+                <div class="grid">
76
+                  <div class="col-12 md:col-6">
77
+                    <AppDropdown
78
+                      label="Status"
79
+                      placeholder="status"
80
+                      :options="optionStatus"
81
+                      :error="form.errors.status"
82
+                      v-model="form.status"
83
+                    />
84
+                  </div>
85
+
86
+                  <div class="col-12 md:col-6">
87
+                    <AppInputText
88
+                      disabled
89
+                      label="Customer"
90
+                      placeholder="customer"
91
+                      v-model="customer.name"
92
+                    />
93
+                  </div>
94
+                </div>
95
+              </template>
96
+            </Card>
97
+          </div>
98
+
99
+          <div class="col-12">
100
+            <Card>
101
+              <template #title>Produk</template>
102
+              <template #content>
103
+                <div class="grid">
104
+                  <div class="col-12 md:col-6">
105
+                    <AppAutoComplete
106
+                      :disabled="!form.customer?.id"
107
+                      empty
108
+                      label="Produk"
109
+                      placeholder="produk"
110
+                      field="name"
111
+                      refresh-data="stockProducts"
112
+                      v-model="form.product"
113
+                      :error="form.errors.products"
114
+                      :suggestions="stockProducts"
115
+                    >
116
+                      <template #item="slotProps">
117
+                        <template v-if="slotProps.item">
118
+                          <div class="flex flex-column">
119
+                            <span>{{ slotProps.item.number }}</span>
120
+                            <span>{{ slotProps.item.name }}</span>
121
+                          </div>
122
+                        </template>
123
+                      </template>
124
+                    </AppAutoComplete>
125
+                  </div>
126
+
127
+                  <div class="col-12 md:col-6">
128
+                    <AppInputText
129
+                      disabled
130
+                      label="Satuan"
131
+                      placeholder="satuan"
132
+                      :value="form.product?.unit"
133
+                    />
134
+                  </div>
135
+
136
+                  <div class="col-12 md:col-6">
137
+                    <AppInputNumber
138
+                      disabled
139
+                      label="Harga Beli"
140
+                      placeholder="harga beli produk"
141
+                      :value="form.product?.price"
142
+                    />
143
+                  </div>
144
+
145
+                  <div class="col-12 md:col-6">
146
+                    <AppInputText
147
+                      disabled
148
+                      label="Stok Tersedia"
149
+                      placeholder="stok tersedia"
150
+                      type="number"
151
+                      :value="form.product?.qty"
152
+                    />
153
+                  </div>
154
+
155
+                  <Divider type="dashed" />
156
+
157
+                  <div class="col-12 md:col-6">
158
+                    <AppInputNumber
159
+                      :disabled="!form.customer?.id"
160
+                      label="Harga Jual"
161
+                      placeholder="harga jual"
162
+                      v-model="form.price"
163
+                    />
164
+                  </div>
165
+
166
+                  <div class="col-12 md:col-6">
167
+                    <AppInputText
168
+                      :disabled="!form.customer?.id"
169
+                      label="Kuantitas"
170
+                      placeholder="kuantitas"
171
+                      type="number"
172
+                      v-model="form.qty"
173
+                    />
174
+                  </div>
175
+                </div>
176
+              </template>
177
+              <template #footer>
178
+                <div class="flex flex-column md:flex-row justify-content-end">
179
+                  <Button
180
+                    label="Tambah Produk"
181
+                    icon="pi pi-check"
182
+                    class="p-button-outlined"
183
+                    :disabled="
184
+                      !form.price || !form.qty || !form.product?.number
185
+                    "
186
+                    @click="onAddProduct"
187
+                  />
188
+                </div>
189
+              </template>
190
+            </Card>
191
+          </div>
192
+
193
+          <div class="col-12">
194
+            <Cart
195
+              title="Keranjang Produk"
196
+              :product-cart="productCart"
197
+              :header-table="cartTable"
198
+              v-model:checked-ppn="form.checkedPpn"
199
+              @delete="onDeleteProduct"
200
+              @edit="onEditProduct"
201
+            />
202
+          </div>
203
+        </div>
76 204
       </div>
77 205
 
78
-      <div class="col-12 lg:col-4">
206
+      <div class="col-12 md:col-4">
79 207
         <Details
80 208
           title="Detail Penjualan"
81
-          :number="sale.number"
82
-          :price="form.price"
83
-          :qty="form.qty"
84
-          :ppn="sale.ppn"
209
+          message="Pastikan semua produk sudah benar"
210
+          :number="number"
85 211
           :status="form.status"
86
-          :person="sale.customer"
87
-          :product="sale.product"
212
+          :person="form.customer"
213
+          :product="form.product"
214
+          :price="totalProductPrice()"
215
+          :disabled="
216
+            form.processing ||
217
+            !form.status ||
218
+            !form.customer?.id ||
219
+            productCart.length === 0
220
+          "
221
+          @submit="onSubmit"
88 222
         />
89 223
       </div>
90 224
     </div>

+ 1
- 0
resources/js/pages/Sales/config.js 查看文件

@@ -13,6 +13,7 @@ export const indexTable = [
13 13
   { field: 'updatedAt', header: 'Tanggal' },
14 14
   { field: 'name', header: 'Nama Pelanggan' },
15 15
   { field: 'phone', header: 'No HP Pelanggan' },
16
+  { field: 'email', header: 'Email Pelanggan' },
16 17
   { field: 'price', header: 'Total Harga' },
17 18
   { field: 'status', header: 'Status' },
18 19
 ]