add print invoice

dev
Aji Kamaludin 1 year ago
parent 3d38e0ec6f
commit e2e64bc1e5
No known key found for this signature in database
GPG Key ID: 19058F67F0083AD3

@ -124,4 +124,11 @@ class SaleController extends Controller
return redirect()->route('sale.index')
->with('message', ['type' => 'success', 'message' => 'Item has beed deleted']);
}
public function invoice(Sale $sale)
{
return view('invoice', [
'sale' => $sale->load(['customer', 'items.product'])
]);
}
}

@ -0,0 +1,42 @@
<?php
namespace App\Http\Controllers;
use App\Models\Setting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class SettingController extends Controller
{
public function index()
{
return inertia('Setting/Index', [
'setting' => Setting::all(),
]);
}
public function update(Request $request)
{
$request->validate([
'name' => 'required|string',
'detail' => 'required|string'
]);
DB::beginTransaction();
foreach ($request->input() as $key => $value) {
Setting::updateOrCreate(
['key' => $key],
[
'value' => $value,
'type' => 'text'
]
);
}
DB::commit();
return redirect()->route('setting.index')
->with('message', ['type' => 'success', 'message' => 'Setting saved']);
}
}

@ -2,6 +2,9 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Carbon;
class Sale extends Model
{
protected $fillable = [
@ -20,4 +23,13 @@ class Sale extends Model
{
return $this->hasMany(SaleItem::class);
}
public function formatedDate(): Attribute
{
return Attribute::make(
get: function () {
return Carbon::parse($this->date)->format('d/m/Y');
}
);
}
}

@ -11,9 +11,14 @@ class Setting extends Model
{
use HasFactory, SoftDeletes, HasUuids;
protected $fillalble = [
protected $fillable = [
'key',
'value',
'type',
];
public static function getValue($key)
{
return Setting::where('key', $key)->value('value');
}
}

@ -50,17 +50,18 @@ class PermissionSeeder extends Seeder
['id' => Str::uuid(), 'label' => 'Update Sale', 'name' => 'update-sale'],
['id' => Str::uuid(), 'label' => 'View Sale', 'name' => 'view-sale'],
['id' => Str::uuid(), 'label' => 'Delete Sale', 'name' => 'delete-sale'],
['id' => Str::uuid(), 'label' => 'View Setting', 'name' => 'view-setting'],
];
foreach($permissions as $permission) {
foreach ($permissions as $permission) {
Permission::insert($permission);
}
$role = Role::create(['name' => 'admin']);
$permissions = Permission::all();
foreach($permissions as $permission) {
foreach ($permissions as $permission) {
$role->rolePermissions()->create(['permission_id' => $permission->id]);
}
@ -77,8 +78,7 @@ class PermissionSeeder extends Seeder
'role_id' => $role->id,
]);
$setting = [
];
$setting = [];
Setting::insert($setting);
}

@ -5,70 +5,80 @@ import {
HiUserGroup,
HiViewList,
HiOutlineCash,
} from "react-icons/hi";
HiCode,
HiOutlineCog,
} from 'react-icons/hi'
export default [
{
name: "Dashboard",
name: 'Dashboard',
show: true,
icon: HiChartPie,
route: route("dashboard"),
active: "dashboard",
permission: "view-dashboard",
route: route('dashboard'),
active: 'dashboard',
permission: 'view-dashboard',
},
{
name: "Transaksi Penjualan",
name: 'Transaksi Penjualan',
show: true,
icon: HiOutlineCash,
route: route("sale.index"),
active: "sale.*",
permission: "view-sale",
route: route('sale.index'),
active: 'sale.*',
permission: 'view-sale',
},
{
name: "Kategori",
name: 'Kategori',
show: true,
icon: HiViewList,
route: route("category.index"),
active: "category.*",
permission: "view-category",
route: route('category.index'),
active: 'category.*',
permission: 'view-category',
},
{
name: "Barang",
name: 'Barang',
show: true,
icon: HiViewList,
route: route("product.index"),
active: "product.*",
permission: "view-product",
route: route('product.index'),
active: 'product.*',
permission: 'view-product',
},
{
name: "Pelangan",
name: 'Pelangan',
show: true,
icon: HiViewList,
route: route("customer.index"),
active: "customer.*",
permission: "view-customer",
route: route('customer.index'),
active: 'customer.*',
permission: 'view-customer',
},
{
name: "User",
name: 'User',
show: true,
icon: HiUser,
items: [
{
name: "Roles",
name: 'Roles',
show: true,
icon: HiUserGroup,
route: route("roles.index"),
active: "roles.*",
permission: "view-role",
route: route('roles.index'),
active: 'roles.*',
permission: 'view-role',
},
{
name: "Users",
name: 'Users',
show: true,
icon: HiUsers,
route: route("user.index"),
active: "user.index",
permission: "view-user",
route: route('user.index'),
active: 'user.index',
permission: 'view-user',
},
],
},
];
{
name: 'Setting',
show: true,
icon: HiOutlineCog,
route: route('setting.index'),
active: 'setting.*',
permission: 'view-setting',
},
]

@ -149,6 +149,10 @@ export default function Sale(props) {
scope="col"
className="py-3 px-6"
/>
<th
scope="col"
className="py-3 px-6"
/>
</tr>
</thead>
<tbody>
@ -172,6 +176,18 @@ export default function Sale(props) {
<td className="py-4 px-6">
{formatIDR(sale.total)}
</td>
<td>
<a
href={route(
'sale.invoice',
sale
)}
target="_blank"
className="text-blue-600 underline"
>
Invoice
</a>
</td>
<td className="py-4 px-6 flex justify-end">
<Dropdown
label={'Opsi'}

@ -0,0 +1,84 @@
import React from 'react'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import FormInput from '@/Components/FormInput'
import Button from '@/Components/Button'
import { Head, useForm } from '@inertiajs/react'
import TextArea from '@/Components/TextArea'
const extractValue = (set, key) => {
const find = set.find((s) => s.key === key)
if (find !== null) {
if (find.type === 'image') {
return find?.url
}
return find?.value
}
return ''
}
export default function Setting(props) {
const { setting } = props
const { data, setData, post, processing, errors } = useForm({
name: extractValue(setting, 'name'),
detail: extractValue(setting, 'detail'),
})
const handleOnChange = (event) => {
setData(
event.target.name,
event.target.type === 'checkbox'
? event.target.checked
? 1
: 0
: event.target.value
)
}
const handleSubmit = () => {
post(route('setting.update'))
}
return (
<AuthenticatedLayout
auth={props.auth}
errors={props.errors}
flash={props.flash}
page={'Setting'}
action={''}
>
<Head title="Setting" />
<div>
<div className="mx-auto sm:px-6 lg:px-8">
<div className="overflow-hidden p-4 shadow-sm sm:rounded-lg bg-white dark:bg-gray-800 flex flex-col">
<div className="text-xl font-bold mb-4">Setting</div>
<FormInput
name="name"
value={data.name}
onChange={handleOnChange}
label="Name"
error={errors.name}
/>
<TextArea
name="detail"
value={data.detail}
onChange={handleOnChange}
label="Detail (address, phone etc)"
rows={8}
error={errors.detail}
/>
<div className="mt-2">
<Button
onClick={handleSubmit}
processing={processing}
>
Simpan
</Button>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
)
}

@ -0,0 +1,171 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title inertia>Invoice</title>
<!-- Fonts -->
<link rel="stylesheet" href="https://fonts.bunny.net/css2?family=Nunito:wght@400;600;700&display=swap">
<!-- Scripts -->
@vite(['resources/css/app.css'])
<script type="text/javascript">
setTimeout(function () { window.print(); }, 500);
window.onfocus = function () { setTimeout(function () { window.close(); }, 500); }
</script>
</head>
<body class="font-sans antialiased p-2">
<div class="font-bold w-full text-3xl">{{ \App\Models\Setting::getValue('name') }}</div>
<div class="w-full flex flex-row space-x-5 mt-5">
<div class="w-2/3">
{!! \App\Models\Setting::getValue('detail') !!}
</div>
<div class="w-1/3 flex flex-col">
<table>
<tbody>
<tr>
<td class="font-bold">FAKTUR #</td>
<td>:</td>
<td>{{ $sale->code }}</td>
</tr>
<tr>
<td class="font-bold">TANGGAL</td>
<td>:</td>
<td>{{ $sale->formated_date }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="text-2xl w-full flex flex-row space-x-2">
<hr class="w-2/3 mt-3.5 border-1 border-gray-600"/>
<div class="w-1/3 flex flex-row space-x-2">
<div>FAKTUR</div>
<hr class="w-full mt-3.5 border-1 border-gray-600"/>
</div>
</div>
<div class="flex flex-row w-full space-x-3">
<div class="flex flex-col w-1/2">
<div>PELANGGAN</div>
<table class="border-2 border-black">
<tbody>
<tr>
<td class="p-1">NAMA</td>
<td class="p-1">{{ $sale->customer?->name }}</td>
</tr>
<tr>
<td class="p-1">ALAMAT</td>
<td class="p-1">{{ $sale->customer?->address }}</td>
</tr>
<tr>
<td class="p-1">TELP</td>
<td class="p-1">{{ $sale->customer?->phone }}</td>
</tr>
<tr>
<td class="p-1">FAX</td>
<td class="p-1"></td>
</tr>
</tbody>
</table>
</div>
<div class="w-1/2">
<div class="invisible">KETERANGAN</div>
<table class="border-2 border-black w-full h-[132px]">
<tbody>
<tr>
<td class="p-1"></td>
<td class="p-1"></td>
</tr>
<tr>
<td class="p-1">JATUH TEMPO</td>
<td class="p-1"></td>
</tr>
<tr>
<td class="p-1"></td>
<td class="p-1"></td>
</tr>
<tr>
<td class="p-1"></td>
<td class="p-1"></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="w-full mt-7">
<table class="w-full">
<tbody>
<tr>
<td class="border border-black font-bold p-1">NO.</td>
<td class="border border-black font-bold p-1">KETERANGAN</td>
<td class="border border-black font-bold p-1 text-right w-1/6">QTY</td>
<td class="border border-black font-bold p-1 text-right w-1/6">HARGA SATUAN (Rp.)</td>
<td class="border border-black font-bold p-1 text-right w-1/6">JUMLAH (Rp.)</td>
</tr>
@foreach($sale->items as $index => $item)
<tr>
<td class="border border-black p-1">{{ $index + 1 }}</td>
<td class="border border-black p-1">{{ $item->product->name }}</td>
<td class="border border-black p-1 text-right">{{ number_format($item->quantity, 0, ',', '.') }}</td>
<td class="border border-black p-1 text-right">{{ number_format($item->price, 0, ',', '.') }}</td>
<td class="border border-black p-1 text-right">{{ number_format($item->quantity * $item->price, 0, ',', '.') }}</td>
</tr>
@endforeach
<tr>
<td class="p-1"></td>
<td class="p-1"></td>
<td class="p-1"></td>
<td class="border border-black p-1">Subtotal</td>
<td class="border border-black p-1 text-right">{{ number_format($sale->total, 0, ',', '.') }}</td>
</tr>
<tr>
<td class="p-1"></td>
<td class="p-1"></td>
<td class="p-1"></td>
<td class="font-bold border border-black p-1">TOTAL</td>
<td class="font-bold border border-black p-1 text-right">{{ number_format($sale->total, 0, ',', '.') }}</td>
</tr>
<tr>
<td class="p-1"></td>
<td class="p-1"></td>
<td class="p-1"></td>
<td class="border border-black p-1">Bayaran Diterima</td>
<td class="border border-black p-1 text-right">{{ number_format($sale->total, 0, ',', '.') }}</td>
</tr>
<tr>
<td class="p-1"></td>
<td class="p-1"></td>
<td class="p-1"></td>
<td class="p-1">Sisa Tagihan</td>
<td class="border border-black p-1 text-right">0</td>
</tr>
</tbody>
</table>
</div>
<div class="w-full mt-10">
<table class="border-collapse border border-black w-2/4">
<tbody>
<tr>
<td class="p-2 font-bold">PESAN</td>
</tr>
<tr class="border-b border-black">
<td class="p-4"></td>
</tr>
<tr>
<td class="p-2">TERBILANG</td>
</tr>
<tr>
<td class="p-4"></td>
</tr>
</tbody>
</table>
</div>
<div class="w-full flex flex-row justify-end mt-24">
<hr class="w-[200px] border-[1px] border-gray-400"/>
</div>
</body>
</html>

@ -8,6 +8,7 @@ use App\Http\Controllers\CategoryController;
use App\Http\Controllers\CustomerController;
use App\Http\Controllers\ProductController;
use App\Http\Controllers\SaleController;
use App\Http\Controllers\SettingController;
use Illuminate\Support\Facades\Route;
/*
@ -62,6 +63,11 @@ Route::middleware(['auth'])->group(function () {
Route::post('/sales', [SaleController::class, 'store'])->name('sale.store');
Route::get('/sales/{sale}', [SaleController::class, 'show'])->name('sale.show');
Route::delete('/sales/{sale}', [SaleController::class, 'destroy'])->name('sale.destroy');
Route::get('/sales/{sale}/invoice', [SaleController::class, 'invoice'])->name('sale.invoice');
// Setting
Route::get('/setting', [SettingController::class, 'index'])->name('setting.index');
Route::post('/setting', [SettingController::class, 'update'])->name('setting.update');
});
Route::middleware('auth')->group(function () {
@ -70,4 +76,4 @@ Route::middleware('auth')->group(function () {
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
require __DIR__.'/auth.php';
require __DIR__ . '/auth.php';

Loading…
Cancel
Save