浏览代码

fix: report master

父节点
当前提交
a0c0c4788d

+ 1
- 1
app/Http/Controllers/CustomerController.php 查看文件

@@ -22,7 +22,7 @@ class CustomerController extends Controller
22 22
     {
23 23
         return inertia("Customers/Index", [
24 24
             "initialSearch" => request("search"),
25
-            "customers" => Customer::filter(request()->only("search"))
25
+            "customers" => Customer::search(request()->only("search"))
26 26
                 ->latest()
27 27
                 ->paginate(10)
28 28
                 ->withQueryString()

+ 1
- 1
app/Http/Controllers/ProductController.php 查看文件

@@ -23,7 +23,7 @@ class ProductController extends Controller
23 23
     {
24 24
         return inertia("Products/Index", [
25 25
             "initialSearch" => request("search"),
26
-            "products" => Product::filter(request()->only("search"))
26
+            "products" => Product::search(request()->only("search"))
27 27
                 ->latest()
28 28
                 ->paginate(10)
29 29
                 ->withQueryString()

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

@@ -34,7 +34,7 @@ class PurchaseController extends Controller
34 34
     {
35 35
         return inertia("Purchases/Index", [
36 36
             "initialSearch" => request("search"),
37
-            "purchases" => Purchase::filter(request()->only("search"))
37
+            "purchases" => Purchase::search(request()->only("search"))
38 38
                 ->latest()
39 39
                 ->paginate(10)
40 40
                 ->withQueryString()
@@ -66,12 +66,12 @@ class PurchaseController extends Controller
66 66
             "ppn" => Ppn::first()->ppn,
67 67
             "productNumber" => "PDK" . now()->format("YmdHis"),
68 68
             "suppliers" => Inertia::lazy(
69
-                fn() => Supplier::filter([
69
+                fn() => Supplier::search([
70 70
                     "search" => request("supplier"),
71 71
                 ])->get()
72 72
             ),
73 73
             "products" => Inertia::lazy(
74
-                fn() => Product::filter(["search" => request("product")])->get()
74
+                fn() => Product::search(["search" => request("product")])->get()
75 75
             ),
76 76
             "productPurchase" => Inertia::lazy(
77 77
                 fn() => PurchaseDetail::historyProductPurchase(
@@ -181,7 +181,7 @@ class PurchaseController extends Controller
181 181
             "ppnChecked" => $purchase->ppn ? true : false,
182 182
             "supplier" => $purchase->supplier,
183 183
             "products" => Inertia::lazy(
184
-                fn() => Product::filter(["search" => request("product")])->get()
184
+                fn() => Product::search(["search" => request("product")])->get()
185 185
             ),
186 186
             "purchaseDetail" => $purchase->purchaseDetail->transform(
187 187
                 fn($purchase) => [

+ 45
- 4
app/Http/Controllers/SalesController.php 查看文件

@@ -12,6 +12,7 @@ use Illuminate\Database\QueryException;
12 12
 use App\Http\Requests\Sales\StoreSaleRequest;
13 13
 use App\Http\Requests\Sales\UpdateSaleRequest;
14 14
 use App\Models\Company;
15
+use App\Models\SaleDetail;
15 16
 use App\Services\FunctionService;
16 17
 use App\Services\SaleService;
17 18
 use Barryvdh\DomPDF\Facade\Pdf;
@@ -32,7 +33,7 @@ class SalesController extends Controller
32 33
     {
33 34
         return inertia("Sales/Index", [
34 35
             "initialSearch" => request("search"),
35
-            "sales" => Sale::filter(request()->only("search"))
36
+            "sales" => Sale::search(request()->only("search"))
36 37
                 ->latest()
37 38
                 ->paginate(10)
38 39
                 ->withQueryString()
@@ -63,12 +64,12 @@ class SalesController extends Controller
63 64
             "number" => "PJN" . now()->format("YmdHis"),
64 65
             "ppn" => Ppn::first()->ppn,
65 66
             "customers" => Inertia::lazy(
66
-                fn() => Customer::filter([
67
+                fn() => Customer::search([
67 68
                     "search" => request("customer"),
68 69
                 ])->get()
69 70
             ),
70 71
             "stockProducts" => Inertia::lazy(
71
-                fn() => StockProduct::filter([
72
+                fn() => StockProduct::search([
72 73
                     "search" => request("stockProduct"),
73 74
                 ])
74 75
                     ->get()
@@ -229,7 +230,47 @@ class SalesController extends Controller
229 230
 
230 231
     public function report()
231 232
     {
232
-        return inertia("Sales/Report");
233
+        return inertia("Sales/Report", [
234
+            "params" => [
235
+                "filters" => request()->only("start_date", "end_date"),
236
+            ],
237
+            "sales" => Inertia::lazy(
238
+                fn() => SaleDetail::filter(
239
+                    request()->only("start_date", "end_date")
240
+                )
241
+                    ->latest()
242
+                    ->paginate(10)
243
+                    ->withQueryString()
244
+                    ->through(
245
+                        fn($saleDetail) => [
246
+                            "updatedAt" => $saleDetail->updated_at,
247
+                            "totalPrice" => FunctionService::rupiahFormat(
248
+                                $saleDetail->price * $saleDetail->qty
249
+                            ),
250
+                            "status" => $saleDetail->sale->status,
251
+                        ]
252
+                    )
253
+            ),
254
+            // "data" => [
255
+            //     "sales" => Inertia::lazy(
256
+            //         fn() => SaleDetail::filter(
257
+            //             request()->only("start_date", "end_date")
258
+            //         )
259
+            //             ->latest()
260
+            //             ->paginate(10)
261
+            //             ->withQueryString()
262
+            //             ->through(
263
+            //                 fn($saleDetail) => [
264
+            //                     "updatedAt" => $saleDetail->updated_at,
265
+            //                     "totalPrice" => FunctionService::rupiahFormat(
266
+            //                         $saleDetail->price * $saleDetail->qty
267
+            //                     ),
268
+            //                     "status" => $saleDetail->sale->status,
269
+            //                 ]
270
+            //             )
271
+            //     ),
272
+            // ],
273
+        ]);
233 274
     }
234 275
 
235 276
     public function reportExcel()

+ 1
- 1
app/Http/Controllers/StockProductController.php 查看文件

@@ -22,7 +22,7 @@ class StockProductController extends Controller
22 22
     {
23 23
         return inertia("StockProducts/Index", [
24 24
             "initialSearch" => request("search"),
25
-            "stockProducts" => StockProduct::filter(request()->only("search"))
25
+            "stockProducts" => StockProduct::search(request()->only("search"))
26 26
                 ->latest()
27 27
                 ->paginate(10)
28 28
                 ->withQueryString()

+ 1
- 1
app/Http/Controllers/UserController.php 查看文件

@@ -26,7 +26,7 @@ class UserController extends Controller
26 26
     {
27 27
         return inertia("Users/Index", [
28 28
             "initialSearch" => request("search"),
29
-            "users" => User::filter(request()->only("search"))
29
+            "users" => User::search(request()->only("search"))
30 30
                 ->latest()
31 31
                 ->paginate(10)
32 32
                 ->withQueryString()

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

@@ -18,7 +18,7 @@ class Customer extends Model
18 18
         return $this->hasMany(Sale::class);
19 19
     }
20 20
 
21
-    public function scopeFilter($query, array $filters)
21
+    public function scopeSearch($query, array $filters)
22 22
     {
23 23
         $query->when($filters["search"] ?? null, function ($query, $search) {
24 24
             $query->where(function ($query) use ($search) {

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

@@ -35,7 +35,7 @@ class Product extends Model
35 35
         return $this->hasMany(SaleDetail::class, "product_number", "number");
36 36
     }
37 37
 
38
-    public function scopeFilter($query, array $filters)
38
+    public function scopeSearch($query, array $filters)
39 39
     {
40 40
         $query->when($filters["search"] ?? null, function ($query, $search) {
41 41
             $query->where(function ($query) use ($search) {

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

@@ -38,7 +38,7 @@ class Purchase extends Model
38 38
         return $this->belongsTo(Supplier::class);
39 39
     }
40 40
 
41
-    public function scopeFilter($query, array $filters)
41
+    public function scopeSearch($query, array $filters)
42 42
     {
43 43
         $query->when($filters["search"] ?? null, function ($query, $search) {
44 44
             $query

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

@@ -34,7 +34,7 @@ class Sale extends Model
34 34
         return $this->belongsTo(Customer::class);
35 35
     }
36 36
 
37
-    public function scopeFilter($query, array $filters)
37
+    public function scopeSearch($query, array $filters)
38 38
     {
39 39
         $query->when($filters["search"] ?? null, function ($query, $search) {
40 40
             $query

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

@@ -2,10 +2,11 @@
2 2
 
3 3
 namespace App\Models;
4 4
 
5
+use Carbon\Carbon;
5 6
 use App\Services\FunctionService;
7
+use Illuminate\Database\Eloquent\Model;
6 8
 use Illuminate\Database\Eloquent\Casts\Attribute;
7 9
 use Illuminate\Database\Eloquent\Factories\HasFactory;
8
-use Illuminate\Database\Eloquent\Model;
9 10
 
10 11
 class SaleDetail extends Model
11 12
 {
@@ -13,6 +14,15 @@ class SaleDetail extends Model
13 14
 
14 15
     protected $fillable = ["price", "qty", "sale_number", "product_number"];
15 16
 
17
+    protected function updatedAt(): Attribute
18
+    {
19
+        return Attribute::make(
20
+            get: fn($value) => Carbon::parse($value)->translatedFormat(
21
+                "l d/m/y"
22
+            )
23
+        );
24
+    }
25
+
16 26
     protected function price(): Attribute
17 27
     {
18 28
         return Attribute::make(
@@ -51,4 +61,15 @@ class SaleDetail extends Model
51 61
                 });
52 62
             });
53 63
     }
64
+
65
+    public function scopeFilter($query, array $filters)
66
+    {
67
+        $query
68
+            ->when($filters["start_date"] ?? null, function ($query, $search) {
69
+                $query->whereDate("created_at", ">=", $search);
70
+            })
71
+            ->when($filters["end_date"] ?? null, function ($query, $search) {
72
+                $query->whereDate("created_at", "<=", $search);
73
+            });
74
+    }
54 75
 }

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

@@ -40,7 +40,7 @@ class StockProduct extends Model
40 40
         return $this->belongsTo(Product::class, "product_number", "number");
41 41
     }
42 42
 
43
-    public function scopeFilter($query, array $filters)
43
+    public function scopeSearch($query, array $filters)
44 44
     {
45 45
         $query
46 46
             ->when($filters["search"] ?? null, function ($query, $search) {

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

@@ -18,7 +18,7 @@ class Supplier extends Model
18 18
         return $this->hasMany(Purchase::class);
19 19
     }
20 20
 
21
-    public function scopeFilter($query, array $filters)
21
+    public function scopeSearch($query, array $filters)
22 22
     {
23 23
         $query->when($filters["search"] ?? null, function ($query, $search) {
24 24
             $query->where(function ($query) use ($search) {

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

@@ -45,7 +45,7 @@ class User extends Authenticatable
45 45
         return $this->belongsTo(Role::class);
46 46
     }
47 47
 
48
-    public function scopeFilter($query, array $filters)
48
+    public function scopeSearch($query, array $filters)
49 49
     {
50 50
         $query->when($filters["search"] ?? null, function ($query, $search) {
51 51
             $query->where(function ($query) use ($search) {

+ 79
- 0
resources/js/components/AppFilterDateRange.vue 查看文件

@@ -0,0 +1,79 @@
1
+<script setup>
2
+import { onMounted, reactive, watch } from 'vue'
3
+import { Inertia } from '@inertiajs/inertia'
4
+import dayjs from 'dayjs'
5
+import { pickBy } from 'lodash'
6
+
7
+const props = defineProps({
8
+  initialFilter: {
9
+    type: Object,
10
+    required: true,
11
+  },
12
+  refreshData: {
13
+    type: String,
14
+    required: true,
15
+  },
16
+})
17
+
18
+const filters = reactive({
19
+  dates: null,
20
+  startDate: null,
21
+  endDate: null,
22
+})
23
+
24
+onMounted(() => {
25
+  if (props.initialFilter.start_date || props.initialFilter.end_date) {
26
+    if (props.initialFilter.end_date) {
27
+      filters.dates = [
28
+        new Date(props.initialFilter.start_date),
29
+        new Date(props.initialFilter.end_date),
30
+      ]
31
+    } else {
32
+      filters.dates = [new Date(props.initialFilter.start_date), null]
33
+    }
34
+  }
35
+})
36
+
37
+watch(
38
+  () => filters.dates,
39
+  () => {
40
+    if (filters.dates[1]) {
41
+      filters.startDate = dayjs(filters.dates[0]).format('YYYY-MM-DD')
42
+
43
+      filters.endDate = dayjs(filters.dates[1]).format('YYYY-MM-DD')
44
+    } else if (filters.dates[0]) {
45
+      filters.startDate = dayjs(filters.dates[0]).format('YYYY-MM-DD')
46
+
47
+      filters.endDate = null
48
+
49
+      removeParams('end_date')
50
+    }
51
+
52
+    Inertia.reload({
53
+      data: pickBy({
54
+        start_date: filters.startDate,
55
+        end_date: filters.endDate,
56
+      }),
57
+      only: [props.refreshData],
58
+    })
59
+  }
60
+)
61
+
62
+const removeParams = (...params) => {
63
+  const urlParams = new URLSearchParams(location.search)
64
+
65
+  params.forEach((value) => urlParams.delete(value))
66
+
67
+  window.history.replaceState({}, '', `${location.pathname}?${urlParams}`)
68
+}
69
+</script>
70
+
71
+<template>
72
+  <Calendar
73
+    class="w-full"
74
+    selection-mode="range"
75
+    date-format="dd/mm/yy"
76
+    :manual-input="false"
77
+    v-model="filters.dates"
78
+  />
79
+</template>

+ 0
- 45
resources/js/composables/useDateRangeFilter.js 查看文件

@@ -1,45 +0,0 @@
1
-import { onMounted, watch, ref } from 'vue'
2
-import dayjs from 'dayjs'
3
-
4
-export function useDateRangeFilter(props) {
5
-  const dates = ref(null)
6
-
7
-  const startDate = ref(null)
8
-
9
-  const endDate = ref(null)
10
-
11
-  onMounted(() => {
12
-    if (!props.initialDateRange) {
13
-      return
14
-    }
15
-
16
-    if (props.initialDateRange[0] || props.initialDateRange[1]) {
17
-      if (props.initialDateRange[1]) {
18
-        dates.value = [
19
-          new Date(props.initialDateRange[0]),
20
-          new Date(props.initialDateRange[1]),
21
-        ]
22
-      } else {
23
-        dates.value = [new Date(props.props.initialDateRange[1]), null]
24
-      }
25
-    }
26
-  })
27
-
28
-  watch(dates, () => {
29
-    if (dates.value[1]) {
30
-      startDate.value = dayjs(dates.value[0]).format('YYYY-MM-DD')
31
-
32
-      endDate.value = dayjs(dates.value[1]).format('YYYY-MM-DD')
33
-    } else if (dates.value[0]) {
34
-      startDate.value = dayjs(dates.value[0]).format('YYYY-MM-DD')
35
-
36
-      endDate.value = null
37
-    }
38
-  })
39
-
40
-  return {
41
-    dates,
42
-    startDate,
43
-    endDate,
44
-  }
45
-}

+ 34
- 1
resources/js/pages/Sales/Report.vue 查看文件

@@ -1,9 +1,42 @@
1 1
 <script setup>
2
+import { reportTable } from './config'
3
+import AppFilterDateRange from '@/components/AppFilterDateRange.vue'
2 4
 import DashboardLayout from '@/layouts/Dashboard/DashboardLayout.vue'
5
+
6
+defineProps({
7
+  params: Object,
8
+  sales: Object,
9
+})
3 10
 </script>
4 11
 
5 12
 <template>
6 13
   <DashboardLayout title="Laporan Penjualan">
7
-    <h1>Laporan Penjualan</h1>
14
+    <DataTable
15
+      responsive-layout="scroll"
16
+      column-resize-mode="expand"
17
+      :rowHover="true"
18
+      :stripedRows="true"
19
+    >
20
+      <template #header>
21
+        <h1>Laporan Penjualan</h1>
22
+
23
+        <div class="grid">
24
+          <div class="col-12 sm:col-6 lg:col-4">
25
+            <AppFilterDateRange
26
+              placeholder="filter waktu..."
27
+              refresh-data="sales"
28
+              :initial-filter="params.filters"
29
+            />
30
+          </div>
31
+        </div>
32
+      </template>
33
+
34
+      <Column
35
+        v-for="value in reportTable"
36
+        :field="value.field"
37
+        :header="value.header"
38
+        :key="value.field"
39
+      />
40
+    </DataTable>
8 41
   </DashboardLayout>
9 42
 </template>

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

@@ -24,3 +24,9 @@ export const cartTable = [
24 24
   { field: 'qty', header: 'Kuantitas' },
25 25
   { field: 'unit', header: 'Satuan' },
26 26
 ]
27
+
28
+export const reportTable = [
29
+  { field: 'updatedAt', header: 'Tanggal' },
30
+  { field: 'totalPrice', header: 'Total Harga' },
31
+  { field: 'status', header: 'Status' },
32
+]

+ 8
- 0
resources/js/utils/helpers.js 查看文件

@@ -22,6 +22,14 @@ export const IDRCurrencyFormat = (number, decimal = false) => {
22 22
   }
23 23
 }
24 24
 
25
+export function removeParams(...params) {
26
+  const urlParams = new URLSearchParams(location.search)
27
+
28
+  params.forEach((value) => urlParams.delete(value))
29
+
30
+  window.history.replaceState({}, '', `${location.pathname}?${urlParams}`)
31
+}
32
+
25 33
 export class FormValidationError extends Error {
26 34
   constructor(message, errors) {
27 35
     super(message)

+ 3
- 3
routes/web.php 查看文件

@@ -54,8 +54,6 @@ Route::middleware(["auth", "verified", "checkBlocked"])->group(function () {
54 54
 
55 55
     Route::resource("/customers", CustomerController::class);
56 56
 
57
-    Route::resource("/purchases", PurchaseController::class);
58
-
59 57
     Route::get("/purchases/pdf/invoice/{purchase}", [
60 58
         PurchaseController::class,
61 59
         "invoice",
@@ -73,7 +71,7 @@ Route::middleware(["auth", "verified", "checkBlocked"])->group(function () {
73 71
 
74 72
     Route::get("/purchases/report", [PurchaseController::class, "report"]);
75 73
 
76
-    Route::resource("/sales", SalesController::class);
74
+    Route::resource("/purchases", PurchaseController::class);
77 75
 
78 76
     Route::get("/sales/pdf/invoice/{sale}", [
79 77
         SalesController::class,
@@ -92,6 +90,8 @@ Route::middleware(["auth", "verified", "checkBlocked"])->group(function () {
92 90
 
93 91
     Route::get("/sales/report", [SalesController::class, "report"]);
94 92
 
93
+    Route::resource("/sales", SalesController::class);
94
+
95 95
     Route::resource("/suppliers", SupplierController::class);
96 96
 
97 97
     Route::resource("/stock-products", StockProductController::class);

+ 0
- 2
storage/framework/cache/data/.gitignore 查看文件

@@ -1,2 +0,0 @@
1
-*
2
-!.gitignore