Quellcode durchsuchen

fix: sale master

Muhammad Iqbal Afandi vor 3 Jahren
Ursprung
Commit
31530dfbd9

+ 1
- 0
app/Http/Controllers/SalesController.php Datei anzeigen

77
                             "ppn" => $stockProduct->ppn,
77
                             "ppn" => $stockProduct->ppn,
78
                             "qty" => $stockProduct->qty,
78
                             "qty" => $stockProduct->qty,
79
                             "unit" => $stockProduct->product->unit,
79
                             "unit" => $stockProduct->product->unit,
80
+                            "profit" => $stockProduct->product->profit,
80
                         ]
81
                         ]
81
                     )
82
                     )
82
             ),
83
             ),

+ 1
- 0
resources/js/components/AppPassword.vue Datei anzeigen

53
     <Password
53
     <Password
54
       class="w-full"
54
       class="w-full"
55
       input-class="w-full"
55
       input-class="w-full"
56
+      :class="{ 'p-invalid': isError }"
56
       :promptLabel="promptLabel"
57
       :promptLabel="promptLabel"
57
       :weakLabel="weakLabel"
58
       :weakLabel="weakLabel"
58
       :mediumLabel="mediumLabel"
59
       :mediumLabel="mediumLabel"

+ 1
- 1
resources/js/pages/Sales/Components/Cart.vue Datei anzeigen

1
 <script setup>
1
 <script setup>
2
 import { ref } from 'vue'
2
 import { ref } from 'vue'
3
-import { IDRCurrencyFormat } from '@/utils/currencyFormat'
3
+import { IDRCurrencyFormat } from '@/utils/helpers'
4
 import AppInputNumber from '@/components/AppInputNumber.vue'
4
 import AppInputNumber from '@/components/AppInputNumber.vue'
5
 import AppInputText from '@/components/AppInputText.vue'
5
 import AppInputText from '@/components/AppInputText.vue'
6
 
6
 

+ 1
- 1
resources/js/pages/Sales/Components/Details.vue Datei anzeigen

1
 <script setup>
1
 <script setup>
2
-import { IDRCurrencyFormat } from '@/utils/currencyFormat'
2
+import { IDRCurrencyFormat } from '@/utils/helpers'
3
 
3
 
4
 defineProps({
4
 defineProps({
5
   title: String,
5
   title: String,

resources/js/pages/Sales/Composables/onShowDialog.js → resources/js/pages/Sales/Composables/useDialog.js Datei anzeigen

1
-import { useDialog } from 'primevue/usedialog'
1
+import { useDialog as usePrimeDialog } from 'primevue/usedialog'
2
 import CustomerCreate from '../Components/CustomerCreate.vue'
2
 import CustomerCreate from '../Components/CustomerCreate.vue'
3
 
3
 
4
-export function onShowDialog() {
5
-  const dialog = useDialog()
4
+export function useDialog() {
5
+  const dialog = usePrimeDialog()
6
 
6
 
7
   const dialogStyle = {
7
   const dialogStyle = {
8
     style: {
8
     style: {

+ 49
- 17
resources/js/pages/Sales/Composables/useProductCart.js Datei anzeigen

1
 import { reactive } from 'vue'
1
 import { reactive } from 'vue'
2
-import FormValidationError from '@/utils/FormValidationError'
2
+import { FormValidationError } from '@/utils/helpers'
3
 
3
 
4
 export function useProductCart(form, initialProducts = []) {
4
 export function useProductCart(form, initialProducts = []) {
5
   const productCart = reactive(initialProducts)
5
   const productCart = reactive(initialProducts)
6
 
6
 
7
   const productCartDeleted = reactive([])
7
   const productCartDeleted = reactive([])
8
 
8
 
9
+  const productErrors = reactive([])
10
+
9
   const productValidation = () => {
11
   const productValidation = () => {
10
-    const existProduct = productCart.find(
11
-      (product) => product.number === form.product.number
12
-    )
12
+    onClearProductErrors()
13
+
14
+    productCart.find((product) => {
15
+      if (product.number === form.product.number) {
16
+        // productErrors.push({
17
+        //   message: 'Produk sudah ada dikeranjang',
18
+        //   field: 'product',
19
+        // })
20
+
21
+        if (form.qty + product.qty > form.product.qty) {
22
+          productErrors.push({
23
+            message: 'Stok tidak mencukupi',
24
+            field: 'qty',
25
+          })
26
+        }
27
+      }
28
+    })
13
 
29
 
14
-    if (existProduct) {
15
-      throw new FormValidationError('Produk sudah ada dikeranjang', 'product')
30
+    if (productErrors.length) {
31
+      throw new FormValidationError('form error', productErrors)
16
     }
32
     }
17
   }
33
   }
18
 
34
 
19
   const onAddProduct = () => {
35
   const onAddProduct = () => {
20
     try {
36
     try {
21
-      form.clearErrors('product', 'price', 'qty')
37
+      form.clearErrors('product', 'qty')
22
 
38
 
23
       productValidation()
39
       productValidation()
24
 
40
 
25
-      productCart.push({
26
-        label: 'add',
27
-        number: form.product.number,
28
-        name: form.product.name,
29
-        price: form.price,
30
-        qty: form.qty,
31
-        unit: form.product.unit,
41
+      productCart.find((product) => {
42
+        if (product.number === form.product.number) {
43
+          product.qty += form.qty
44
+        } else {
45
+          productCart.push({
46
+            label: 'add',
47
+            number: form.product.number,
48
+            name: form.product.name,
49
+            price: form.price,
50
+            qty: form.qty,
51
+            unit: form.product.unit,
52
+          })
53
+        }
32
       })
54
       })
33
 
55
 
34
-      form.reset('product', 'price', 'qty')
56
+      form.reset('product', 'qty')
35
     } catch (e) {
57
     } catch (e) {
36
-      form.setError(e.field, e.message)
58
+      e.errors.forEach((error) => {
59
+        form.setError(error.field, error.message)
60
+      })
37
     }
61
     }
38
   }
62
   }
39
 
63
 
56
     productCartDeleted.splice(0)
80
     productCartDeleted.splice(0)
57
   }
81
   }
58
 
82
 
83
+  const onClearProductErrors = () => {
84
+    productErrors.splice(0)
85
+  }
86
+
59
   const totalProductPrice = () => {
87
   const totalProductPrice = () => {
60
     const productPrices = productCart.map((product) => {
88
     const productPrices = productCart.map((product) => {
61
       if (form.checkedPpn) {
89
       if (form.checkedPpn) {
65
       }
93
       }
66
     })
94
     })
67
 
95
 
68
-    return productPrices.reduce((prev, current) => prev + current, 0)
96
+    return productPrices.reduce(
97
+      (prevPrice, currentPrice) => prevPrice + currentPrice,
98
+      0
99
+    )
69
   }
100
   }
70
 
101
 
71
   const onEditProduct = (event) => {
102
   const onEditProduct = (event) => {
80
   return {
111
   return {
81
     productCart,
112
     productCart,
82
     productCartDeleted,
113
     productCartDeleted,
114
+    productErrors,
83
     onClearProductCart,
115
     onClearProductCart,
84
     onClearProductCartDelete,
116
     onClearProductCartDelete,
85
     onAddProduct,
117
     onAddProduct,

+ 27
- 10
resources/js/pages/Sales/Create.vue Datei anzeigen

1
 <script setup>
1
 <script setup>
2
-import { computed } from 'vue'
2
+import { computed, watchEffect } from 'vue'
3
+import { profit } from '@/utils/helpers'
3
 import { optionStatus } from './config'
4
 import { optionStatus } from './config'
4
 import { cartTable } from './config'
5
 import { cartTable } from './config'
5
 import Details from './Components/Details.vue'
6
 import Details from './Components/Details.vue'
6
 import Cart from './Components/Cart.vue'
7
 import Cart from './Components/Cart.vue'
7
 import { useProductCart } from './Composables/useProductCart'
8
 import { useProductCart } from './Composables/useProductCart'
8
-import { onShowDialog } from './Composables/onShowDialog'
9
+import { useDialog } from './Composables/useDialog'
9
 import { useForm } from '@/composables/useForm'
10
 import { useForm } from '@/composables/useForm'
10
 import AppInputText from '@/components/AppInputText.vue'
11
 import AppInputText from '@/components/AppInputText.vue'
11
 import AppInputNumber from '@/components/AppInputNumber.vue'
12
 import AppInputNumber from '@/components/AppInputNumber.vue'
54
     })
55
     })
55
 }
56
 }
56
 
57
 
58
+watchEffect(() => {
59
+  if (form.product?.number) {
60
+    form.price = profit(form.product.price, form.product.profit)
61
+
62
+    form.qty = 1
63
+  }
64
+})
65
+
57
 const dropdownStatus = computed(() => {
66
 const dropdownStatus = computed(() => {
58
   return optionStatus.filter((val) => val.value != 'pending')
67
   return optionStatus.filter((val) => val.value != 'pending')
59
 })
68
 })
60
 
69
 
70
+const profitDescription = computed(() => {
71
+  const priceProfit = profit(form.product?.price, form.product?.profit)
72
+
73
+  return form.price === priceProfit ? true : false
74
+})
75
+
61
 const {
76
 const {
62
   productCart,
77
   productCart,
78
+  productErrors,
63
   onAddProduct,
79
   onAddProduct,
64
   onClearProductCart,
80
   onClearProductCart,
65
   onDeleteProduct,
81
   onDeleteProduct,
66
-  onEditProduct,
67
   totalProductPrice,
82
   totalProductPrice,
68
 } = useProductCart(form)
83
 } = useProductCart(form)
69
 
84
 
70
-const { onShowCustomerCreate } = onShowDialog()
85
+const { onShowCustomerCreate } = useDialog()
71
 </script>
86
 </script>
72
 
87
 
73
 <template>
88
 <template>
142
                       field="name"
157
                       field="name"
143
                       refresh-data="stockProducts"
158
                       refresh-data="stockProducts"
144
                       v-model="form.product"
159
                       v-model="form.product"
145
-                      :error="form.errors.products"
160
+                      :error="form.errors.product"
146
                       :suggestions="stockProducts"
161
                       :suggestions="stockProducts"
147
                     >
162
                     >
148
                       <template #item="slotProps">
163
                       <template #item="slotProps">
202
                   <div class="col-12 md:col-6">
217
                   <div class="col-12 md:col-6">
203
                     <AppInputNumber
218
                     <AppInputNumber
204
                       class="m-0"
219
                       class="m-0"
205
-                      :disabled="!form.customer?.id"
220
+                      :disabled="!form.product?.number"
206
                       label="Harga Jual"
221
                       label="Harga Jual"
207
                       placeholder="harga jual"
222
                       placeholder="harga jual"
208
                       v-model="form.price"
223
                       v-model="form.price"
209
                     />
224
                     />
210
-                    <span v-if="form.product?.number" class="text-xs">
211
-                      Sudah termasuk profit 30%
225
+                    <span v-if="profitDescription" class="text-xs">
226
+                      Sudah termasuk profit {{ form.product.profit }} %
212
                     </span>
227
                     </span>
213
                   </div>
228
                   </div>
214
 
229
 
215
                   <div class="col-12 md:col-6">
230
                   <div class="col-12 md:col-6">
216
                     <AppInputText
231
                     <AppInputText
217
-                      :disabled="!form.customer?.id"
232
+                      :disabled="!form.product?.number"
218
                       label="Kuantitas"
233
                       label="Kuantitas"
219
                       placeholder="kuantitas"
234
                       placeholder="kuantitas"
220
                       type="number"
235
                       type="number"
236
+                      :error="form.errors.qty"
221
                       v-model="form.qty"
237
                       v-model="form.qty"
222
                     />
238
                     />
223
                   </div>
239
                   </div>
229
                     label="Tambah Produk"
245
                     label="Tambah Produk"
230
                     icon="pi pi-check"
246
                     icon="pi pi-check"
231
                     class="p-button-outlined"
247
                     class="p-button-outlined"
248
+                    :class="{ 'p-button-danger': productErrors.length }"
232
                     :disabled="
249
                     :disabled="
233
                       !form.price || !form.qty || !form.product?.number
250
                       !form.price || !form.qty || !form.product?.number
234
                     "
251
                     "
244
               title="Keranjang Produk"
261
               title="Keranjang Produk"
245
               :product-cart="productCart"
262
               :product-cart="productCart"
246
               :header-table="cartTable"
263
               :header-table="cartTable"
264
+              :btn-edit-show="false"
247
               v-model:checked-ppn="form.checkedPpn"
265
               v-model:checked-ppn="form.checkedPpn"
248
               @delete="onDeleteProduct"
266
               @delete="onDeleteProduct"
249
-              @edit="onEditProduct"
250
             />
267
             />
251
           </div>
268
           </div>
252
         </div>
269
         </div>

+ 30
- 0
resources/js/utils/helpers.js Datei anzeigen

1
+export function ppn(price, percent) {
2
+  return price + price * (percent / 100)
3
+}
4
+
5
+export function profit(price, percent) {
6
+  return ppn(price, percent)
7
+}
8
+
9
+export function discount(price, percent) {
10
+  return price - price * (percent / 100)
11
+}
12
+
13
+export const IDRCurrencyFormat = (number, decimal = false) => {
14
+  if (number === null) {
15
+    return
16
+  }
17
+
18
+  if (decimal) {
19
+    return 'Rp' + number.toLocaleString('id') + ',00'
20
+  } else {
21
+    return 'Rp' + number.toLocaleString('id')
22
+  }
23
+}
24
+
25
+export class FormValidationError extends Error {
26
+  constructor(message, errors) {
27
+    super(message)
28
+    this.errors = errors
29
+  }
30
+}