diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..9c2d622 --- /dev/null +++ b/TODO.md @@ -0,0 +1 @@ +1. Pada Transaksi Penjualan bisa ada tambahan menu Print - AutoGenerate Invoice diff --git a/app/Http/Controllers/GeneralController.php b/app/Http/Controllers/GeneralController.php index cb4a8f6..ee2460e 100644 --- a/app/Http/Controllers/GeneralController.php +++ b/app/Http/Controllers/GeneralController.php @@ -14,6 +14,11 @@ class GeneralController extends Controller public function index(Request $request) { $totalSaleToday = Sale::where('date', now()->format('m/d/Y'))->count(); + $totalSaleMonth = Sale::whereBetween('date', [ + now()->startOfMonth()->format('m/d/Y'), + now()->endOfMonth()->format('m/d/Y') + ])->sum('total'); + $totalItem = SaleItem::whereHas('sale', function ($q) { return $q->where('date', now()->format('m/d/Y')); })->sum('quantity'); @@ -72,7 +77,9 @@ class GeneralController extends Controller 'sale_days' => $charts, 'favorite_categories' => $dounat, 'list_favorite_product' => $favoriteProducts, - 'list_customer' => $transactionCustomers + 'list_customer' => $transactionCustomers, + 'month' => 'Mei', + 'total_sale_month' => $totalSaleMonth, ]); } diff --git a/app/Http/Controllers/SaleController.php b/app/Http/Controllers/SaleController.php index e2301c4..2cafdc0 100644 --- a/app/Http/Controllers/SaleController.php +++ b/app/Http/Controllers/SaleController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; use App\Models\Product; use App\Models\Sale; use Illuminate\Http\Request; +use Illuminate\Support\Carbon; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; @@ -18,10 +19,26 @@ class SaleController extends Controller $query->where('code', 'like', "%{$request->q}%"); } + $startDate = now()->startOfMonth()->format('m/d/Y'); + $endDate = now()->endOfMonth()->format('m/d/Y'); + + if ($request->startDate != '' && $request->endDate != '') { + $startDate = Carbon::parse($request->startDate)->format('m/d/Y'); + $endDate = Carbon::parse($request->endDate)->format('m/d/Y'); + } + + $query->whereBetween('date', [$startDate, $endDate]); + + if ($request->customer_id != '') { + $query->where('customer_id', $request->customer_id); + } + $query->orderBy('date', 'desc'); - + return inertia('Sale/Index', [ 'query' => $query->paginate(10), + '_startDate' => $startDate, + '_endDate' => $endDate ]); } @@ -50,14 +67,17 @@ class SaleController extends Controller ]); DB::beginTransaction(); + + $code = 'INV' . now()->format('dmY') . '-' . Sale::where('date', now()->format('m/d/Y'))->count(); + $sale = Sale::create([ - 'code' => Str::upper(Str::random(6)), - 'date' => $request->date, + 'code' => $code, + 'date' => $request->date, 'customer_id' => $request->customer_id, 'total' => collect($request->items)->sum(fn ($item) => $item['qty'] * $item['price']) ]); - foreach($request->items as $item) { + foreach ($request->items as $item) { $sale->items()->create([ "product_id" => $item['id'], "price" => $item['price'], @@ -65,7 +85,7 @@ class SaleController extends Controller "quantity" => $item['qty'], ]); - $product = Product::where('id', $item['id'])->first(); + $product = Product::where('id', $item['id'])->first(); $stock = $product->stock - $item['qty']; if ($stock < 0) { DB::rollBack(); @@ -81,7 +101,7 @@ class SaleController extends Controller ->with('message', ['type' => 'success', 'message' => 'Item has beed saved']); } - public function show(Sale $sale) + public function show(Sale $sale) { return inertia('Sale/Show', [ 'sale' => $sale->load(['items.product', 'customer']), diff --git a/composer.json b/composer.json index 289fc97..a766a17 100644 --- a/composer.json +++ b/composer.json @@ -6,23 +6,23 @@ "license": "MIT", "require": { "php": "^8.1.0", - "guzzlehttp/guzzle": "^7.5.1", + "guzzlehttp/guzzle": "^7.7.0", "inertiajs/inertia-laravel": "^0.6.9", - "laravel/framework": "^10.10.0", + "laravel/framework": "^10.11.0", "laravel/sanctum": "^3.2.5", "laravel/tinker": "^2.8.1", "react/async": "^4", - "tightenco/ziggy": "^1.5.2" + "tightenco/ziggy": "^1.6.0" }, "require-dev": { "beyondcode/laravel-dump-server": "^1.9", - "fakerphp/faker": "^1.21.0", + "fakerphp/faker": "^1.22.0", "laravel/breeze": "^1.21.0", "laravel/pint": "^1.10", "laravel/sail": "^1.22.0", "mockery/mockery": "^1.5.1", "nunomaduro/collision": "^6.4", - "phpunit/phpunit": "^9.6.7", + "phpunit/phpunit": "^9.6.8", "spatie/laravel-ignition": "^2.1.2" }, "autoload": { diff --git a/composer.lock b/composer.lock index 285ff45..10d5aa8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "18bdff683efe13f43d3bb4a931a4fb1e", + "content-hash": "148b8a6d9c79b3f2513c09b12bcdbb3b", "packages": [ { "name": "brick/math", @@ -567,21 +567,21 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.5.1", + "version": "7.7.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9" + "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b964ca597e86b752cd994f27293e9fa6b6a95ed9", - "reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5", + "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5", + "guzzlehttp/promises": "^1.5.3 || ^2.0", "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", @@ -593,7 +593,8 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.1", "ext-curl": "*", - "php-http/client-integration-tests": "^3.0", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", "phpunit/phpunit": "^8.5.29 || ^9.5.23", "psr/log": "^1.1 || ^2.0 || ^3.0" }, @@ -607,9 +608,6 @@ "bamarni-bin": { "bin-links": true, "forward-command": false - }, - "branch-alias": { - "dev-master": "7.5-dev" } }, "autoload": { @@ -675,7 +673,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.5.1" + "source": "https://github.com/guzzle/guzzle/tree/7.7.0" }, "funding": [ { @@ -691,38 +689,37 @@ "type": "tidelift" } ], - "time": "2023-04-17T16:30:08+00:00" + "time": "2023-05-21T14:04:53+00:00" }, { "name": "guzzlehttp/promises", - "version": "1.5.2", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "b94b2807d85443f9719887892882d0329d1e2598" + "reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", - "reference": "b94b2807d85443f9719887892882d0329d1e2598", + "url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6", + "reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "^7.2.5 || ^8.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" + "bamarni/composer-bin-plugin": "^1.8.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.5-dev" + "bamarni-bin": { + "bin-links": true, + "forward-command": false } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\Promise\\": "src/" } @@ -759,7 +756,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.2" + "source": "https://github.com/guzzle/promises/tree/2.0.0" }, "funding": [ { @@ -775,7 +772,7 @@ "type": "tidelift" } ], - "time": "2022-08-28T14:55:35+00:00" + "time": "2023-05-21T13:50:22+00:00" }, { "name": "guzzlehttp/psr7", @@ -1051,16 +1048,16 @@ }, { "name": "laravel/framework", - "version": "v10.10.0", + "version": "v10.11.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "0da22a8d179f79b49d4e71f4822f759651f35012" + "reference": "21a5b6d9b669f32c10cc8ba776511b5f62599fea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/0da22a8d179f79b49d4e71f4822f759651f35012", - "reference": "0da22a8d179f79b49d4e71f4822f759651f35012", + "url": "https://api.github.com/repos/laravel/framework/zipball/21a5b6d9b669f32c10cc8ba776511b5f62599fea", + "reference": "21a5b6d9b669f32c10cc8ba776511b5f62599fea", "shasum": "" }, "require": { @@ -1247,7 +1244,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-05-09T13:08:05+00:00" + "time": "2023-05-16T13:59:23+00:00" }, { "name": "laravel/sanctum", @@ -2189,16 +2186,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.4", + "version": "v4.15.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", "shasum": "" }, "require": { @@ -2239,9 +2236,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" }, - "time": "2023-03-05T19:49:14+00:00" + "time": "2023-05-19T20:20:00+00:00" }, { "name": "nunomaduro/termwind", @@ -5488,16 +5485,16 @@ }, { "name": "tightenco/ziggy", - "version": "v1.5.2", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/tighten/ziggy.git", - "reference": "5c4399c820e66c8e46e779cfb5231ceef6ddc9c5" + "reference": "3beb080be60b1eadad043f3773a160df13fa215f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tighten/ziggy/zipball/5c4399c820e66c8e46e779cfb5231ceef6ddc9c5", - "reference": "5c4399c820e66c8e46e779cfb5231ceef6ddc9c5", + "url": "https://api.github.com/repos/tighten/ziggy/zipball/3beb080be60b1eadad043f3773a160df13fa215f", + "reference": "3beb080be60b1eadad043f3773a160df13fa215f", "shasum": "" }, "require": { @@ -5549,9 +5546,9 @@ ], "support": { "issues": "https://github.com/tighten/ziggy/issues", - "source": "https://github.com/tighten/ziggy/tree/v1.5.2" + "source": "https://github.com/tighten/ziggy/tree/v1.6.0" }, - "time": "2023-05-05T17:52:13+00:00" + "time": "2023-05-12T20:08:56+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -5961,16 +5958,16 @@ }, { "name": "fakerphp/faker", - "version": "v1.21.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d" + "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/92efad6a967f0b79c499705c69b662f738cc9e4d", - "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/f85772abd508bd04e20bb4b1bbe260a68d0066d2", + "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2", "shasum": "" }, "require": { @@ -6023,9 +6020,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.21.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.22.0" }, - "time": "2022-12-13T13:54:32+00:00" + "time": "2023-05-14T12:31:37+00:00" }, { "name": "filp/whoops", @@ -6992,16 +6989,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.7", + "version": "9.6.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2" + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", - "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e", + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e", "shasum": "" }, "require": { @@ -7075,7 +7072,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.7" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.8" }, "funding": [ { @@ -7091,7 +7088,7 @@ "type": "tidelift" } ], - "time": "2023-04-14T08:58:40+00:00" + "time": "2023-05-11T05:14:45+00:00" }, { "name": "sebastian/cli-parser", diff --git a/package-lock.json b/package-lock.json index 2ecf5d6..3915e1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "simple-pos", + "name": "laravel-simplepos", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/resources/js/Components/FormInputDateRange.jsx b/resources/js/Components/FormInputDateRange.jsx new file mode 100644 index 0000000..fbf008e --- /dev/null +++ b/resources/js/Components/FormInputDateRange.jsx @@ -0,0 +1,56 @@ +import React from 'react' +import DatePicker from 'react-datepicker' +import { converToDate, dateToString } from '@/utils' + +export default function FormInputDateRanger({ + selected, + onChange, + label = '', + error, + placeholder = '', +}) { + return ( +
+ {label !== '' && ( + + )} + { + let startDate = dateToString(date[0]) + let endDate = null + if (date[1] != null) { + endDate = dateToString(date[1]) + } + onChange({ startDate, endDate }) + }} + startDate={converToDate(selected.startDate)} + endDate={converToDate(selected.endDate)} + closeOnScroll={true} + shouldCloseOnSelect={true} + dateFormat="dd/MM/yyyy" + className={`mb-2 bg-gray-50 border text-gray-900 text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:placeholder-gray-400 dark:text-white ${ + error + ? 'border-red-500 dark:border-red-500 focus:ring-red-500 focus:border-red-500 dark:focus:ring-red-500 dark:focus:border-red-500' + : 'border-gray-300 dark:border-gray-600 focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-500 dark:focus:border-blue-500' + }`} + nextMonthButtonLabel=">" + previousMonthButtonLabel="<" + nextYearButtonLabel=">" + previousYearButtonLabel="<" + placeholderText={placeholder} + selectsRange + /> + {error && ( +

+ {error} +

+ )} +
+ ) +} diff --git a/resources/js/Pages/Dashboard.jsx b/resources/js/Pages/Dashboard.jsx index 3325ed8..03d637c 100644 --- a/resources/js/Pages/Dashboard.jsx +++ b/resources/js/Pages/Dashboard.jsx @@ -34,6 +34,8 @@ export default function Dashboard(props) { list_favorite_product, list_customer, favorite_categories, + month, + total_sale_month, } = props const options = { @@ -137,10 +139,10 @@ export default function Dashboard(props) {
Total Penjualan
- Hari Ini + Bulan {month}
- {total_sale_today} + Rp. {formatIDR(total_sale_month)}
diff --git a/resources/js/Pages/Sale/Index.jsx b/resources/js/Pages/Sale/Index.jsx index a7d2697..b7046c8 100644 --- a/resources/js/Pages/Sale/Index.jsx +++ b/resources/js/Pages/Sale/Index.jsx @@ -1,22 +1,34 @@ -import React, { useEffect, useState } from 'react'; -import { router } from '@inertiajs/react'; -import { usePrevious } from 'react-use'; -import { Head } from '@inertiajs/react'; -import { Button, Dropdown } from 'flowbite-react'; -import { HiEye, HiTrash } from 'react-icons/hi'; -import { useModalState } from '@/hooks'; +import React, { useEffect, useState } from 'react' +import { router } from '@inertiajs/react' +import { usePrevious } from 'react-use' +import { Head } from '@inertiajs/react' +import { Button, Dropdown } from 'flowbite-react' +import { HiEye, HiTrash } from 'react-icons/hi' +import { useModalState } from '@/hooks' -import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; -import Pagination from '@/Components/Pagination'; -import ModalConfirm from '@/Components/ModalConfirm'; -import SearchInput from '@/Components/SearchInput'; -import { formatDate, formatIDR, hasPermission } from '@/utils'; +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import Pagination from '@/Components/Pagination' +import ModalConfirm from '@/Components/ModalConfirm' +import SearchInput from '@/Components/SearchInput' +import { formatDate, formatIDR, hasPermission } from '@/utils' +import CustomerSelectionInput from '../Customer/SelectionInput' +import FormInputDateRanger from '@/Components/FormInputDateRange' export default function Sale(props) { - const { query: { links, data }, auth } = props - + const { + query: { links, data }, + _startDate, + _endDate, + auth, + } = props + + const [customer, setCustomer] = useState(null) + const [date, setDate] = useState({ + startDate: _startDate, + endDate: _endDate, + }) const [search, setSearch] = useState('') - const preValue = usePrevious(search) + const preValue = usePrevious(`${search}${customer}${date}`) const confirmModal = useModalState() @@ -34,24 +46,28 @@ export default function Sale(props) { } const onDelete = () => { - if(confirmModal.data !== null) { + if (confirmModal.data !== null) { router.delete(route('sale.destroy', confirmModal.data.id)) } } - const params = { q: search } + const params = { q: search, customer_id: customer, ...date } useEffect(() => { if (preValue) { + let filterDate = {} + if (date.endDate !== null) { + filterDate = date + } router.get( route(route().current()), - { q: search }, + { q: search, customer_id: customer, ...filterDate }, { replace: true, preserveState: true, } ) } - }, [search]) + }, [search, customer, date]) const canCreate = hasPermission(auth, 'create-sale') const canUpdate = hasPermission(auth, 'update-sale') @@ -70,41 +86,81 @@ export default function Sale(props) {
-
+
{canCreate && ( - + )} -
+
setSearch(e.target.value)} + onChange={(e) => setSearch(e.target.value)} value={search} /> +
+ setCustomer(id)} + /> + { + setDate(date) + }} + placeholder="Tanggal" + /> +
-
+
- - - - - - {data.map(sale => ( - - +
+ Kode + Tanggal + Pelanggan + Total +
+ {data.map((sale) => ( +
{sale.code} @@ -118,25 +174,41 @@ export default function Sale(props) { {canUpdate && ( - viewDetail(sale)}> -
- -
Detail
+ + viewDetail( + sale + ) + } + > +
+ +
+ Detail +
)} {canDelete && ( - handleDeleteClick(sale)}> -
- -
Hapus
+ + handleDeleteClick( + sale + ) + } + > +
+ +
+ Hapus +
)} @@ -147,17 +219,14 @@ export default function Sale(props) {
-
- +
+
- + - ); -} \ No newline at end of file + ) +}