voucher harga per level

dev
Aji Kamaludin 1 year ago
parent 612ccc0ac1
commit 6e5221e83f
No known key found for this signature in database
GPG Key ID: 19058F67F0083AD3

@ -19,9 +19,9 @@
- [x] Voucher Sales (index: customer, total, jumlah voucher, detail: customer, list voucher, payment)
- [x] Dashboard (gafik hasil penjualan : disorting tanggal, lokasi dan customer)
- [x] Notification ([x]manual deposit, [x]deposit success, [x]stock voucher, [x]sale)
- [ ] View Customer Coin History
- [ ] Voucher - harga per level
- [x] Voucher - harga per level , fixing detail transaksi
- [ ] Voucher - harga coin
- [ ] View Customer Coin History
### Adds

@ -25,7 +25,7 @@ class CartController extends Controller
{
$carts = collect(session('carts') ?? []);
$total = $carts->sum(function ($item) {
return $item['quantity'] * $item['voucher']->price;
return $item['quantity'] * $item['voucher']->validate_price;
});
$customer = Customer::find(auth()->id());
@ -119,7 +119,7 @@ class CartController extends Controller
};
$total = $carts->sum(function ($item) {
return $item['quantity'] * $item['voucher']->price;
return $item['quantity'] * $item['voucher']->validate_price;
});
$customer = Customer::find(auth()->id());
@ -149,7 +149,7 @@ class CartController extends Controller
$sale->items()->create([
'entity_type' => $voucher::class,
'entity_id' => $voucher->id,
'price' => $voucher->price,
'price' => $voucher->validate_price,
'quantity' => 1,
'additional_info_json' => json_encode($item),
]);

@ -79,6 +79,16 @@ class GeneralController extends Controller
$charts->where('customer_id', $request->customer_id);
}
$ca = [];
$date = Carbon::parse($startDate);
$end = Carbon::parse($endDate);
$data = $charts->get();
while ($date <= $end) {
$da = $data->where('date', $date->format('d/m/Y'))?->value('sale_total') ?? 0;
$ca[] = ['sale_total' => $da, 'date' => $date->format('d/m/Y')];
$date = $date->addDay();
}
return inertia('Dashboard', [
'total_voucher' => $total_voucher,
'total_customer' => $total_customer,
@ -91,9 +101,11 @@ class GeneralController extends Controller
'month' => $month,
'deposites' => $deposites,
'sales' => $sales,
'charts' => $charts->get(),
'charts' => $ca,
'_startDate' => $startDate,
'_endDate' => $endDate,
'c' => $charts,
'd' => $data,
]);
}

@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Models\CustomerLevel;
use App\Models\Voucher;
use App\Services\GeneralService;
use Illuminate\Http\Request;
@ -31,7 +32,9 @@ class VoucherController extends Controller
public function create()
{
return inertia('Voucher/Form');
return inertia('Voucher/Form', [
'levels' => CustomerLevel::all()
]);
}
public function store(Request $request)
@ -49,9 +52,13 @@ class VoucherController extends Controller
'comment' => 'required|string',
'expired' => 'required|numeric',
'expired_unit' => 'required|string',
'prices' => 'nullable|array',
'prices.*.customer_level_id' => 'required|exists:customer_levels,id',
'prices.*.display_price' => 'required|numeric'
]);
Voucher::create([
DB::beginTransaction();
$voucher = Voucher::create([
'name' => $request->name,
'description' => $request->description,
'location_id' => $request->location_id,
@ -66,6 +73,19 @@ class VoucherController extends Controller
'expired_unit' => $request->expired_unit,
]);
foreach (collect($request->prices) as $price) {
$finalPrice = $price['display_price'];
if ($voucher->discount > 0) {
$finalPrice = $finalPrice - round($finalPrice * ($voucher->discount / 100), 2);
}
$voucher->prices()->create([
'customer_level_id' => $price['customer_level_id'],
'price' => $finalPrice,
'display_price' => $price['display_price'],
]);
}
DB::commit();
return redirect()->route('voucher.index')
->with('message', ['type' => 'success', 'message' => 'Item has beed saved']);
}
@ -73,7 +93,8 @@ class VoucherController extends Controller
public function edit(Voucher $voucher)
{
return inertia('Voucher/Form', [
'voucher' => $voucher,
'voucher' => $voucher->load(['prices.level']),
'levels' => CustomerLevel::all()
]);
}
@ -92,8 +113,12 @@ class VoucherController extends Controller
'comment' => 'required|string',
'expired' => 'required|numeric',
'expired_unit' => 'required|string',
'prices' => 'nullable|array',
'prices.*.customer_level_id' => 'required|exists:customer_levels,id',
'prices.*.display_price' => 'required|numeric'
]);
DB::beginTransaction();
$voucher->update([
'name' => $request->name,
'description' => $request->description,
@ -108,6 +133,20 @@ class VoucherController extends Controller
'expired' => $request->expired,
]);
$voucher->prices()->delete();
foreach (collect($request->prices) as $price) {
$finalPrice = $price['display_price'];
if ($voucher->discount > 0) {
$finalPrice = $finalPrice - round($finalPrice * ($voucher->discount / 100), 2);
}
$voucher->prices()->create([
'customer_level_id' => $price['customer_level_id'],
'price' => $finalPrice,
'display_price' => $price['display_price'],
]);
}
DB::commit();
return redirect()->route('voucher.index')
->with('message', ['type' => 'success', 'message' => 'Item has beed updated']);
}
@ -122,7 +161,9 @@ class VoucherController extends Controller
public function form_import()
{
return inertia('Voucher/Import');
return inertia('Voucher/Import', [
'levels' => CustomerLevel::all()
]);
}
public function import(Request $request)
@ -134,6 +175,9 @@ class VoucherController extends Controller
'display_price' => 'required|numeric',
'expired' => 'required|numeric',
'expired_unit' => 'required|string',
'prices' => 'nullable|array',
'prices.*.customer_level_id' => 'required|exists:customer_levels,id',
'prices.*.display_price' => 'required|numeric'
]);
$batchId = Str::ulid();
@ -141,7 +185,7 @@ class VoucherController extends Controller
DB::beginTransaction();
foreach ($vouchers as $voucher) {
Voucher::create([
$voucher = Voucher::create([
'location_id' => $request->location_id,
'username' => $voucher['username'],
'password' => $voucher['password'],
@ -154,6 +198,18 @@ class VoucherController extends Controller
'expired_unit' => $request->expired_unit,
'batch_id' => $batchId,
]);
foreach (collect($request->prices) as $price) {
$finalPrice = $price['display_price'];
if ($voucher->discount > 0) {
$finalPrice = $finalPrice - round($finalPrice * ($voucher->discount / 100), 2);
}
$voucher->prices()->create([
'customer_level_id' => $price['customer_level_id'],
'price' => $finalPrice,
'display_price' => $price['display_price'],
]);
}
}
DB::commit();

@ -37,17 +37,19 @@ class SaleItem extends Model
public function shareWord(): Attribute
{
return Attribute::make(get: function () {
$string = "Hai, aku baru beli voucher {$this->voucher->location->name} di " . route('home.index');
$string .= " voucher {$this->voucher->display_quota} buat {$this->voucher->display_expired}
$item = json_decode($this->additional_info_json);
ds($item);
$string = "Hai, aku baru beli voucher {$item->voucher->location->name} di " . route('home.index');
$string .= " voucher {$item->voucher->display_quota} buat {$item->voucher->display_expired}
Username : {$this->voucher->username}
Password : {$this->voucher->password}
Username : {$item->voucher->username}
Password : {$item->voucher->password}
";
$string .= "Cuman Rp" . number_format($this->price, '0', ',', '.') . " aja, ";
if ($this->voucher->discount > 0) {
$string .= "lagi ada discount {$this->voucher->discount}% loh.
if ($item->voucher->discount > 0) {
$string .= "lagi ada discount {$item->voucher->discount}% loh.
";
}

@ -32,7 +32,7 @@ class Voucher extends Model
'batch_id',
];
protected $appends = ['display_quota', 'display_expired'];
protected $appends = ['display_quota', 'display_expired', 'validate_price', 'validate_display_price'];
protected static function booted(): void
{
@ -74,11 +74,44 @@ class Voucher extends Model
});
}
public function validatePrice(): Attribute
{
return Attribute::make(get: function () {
if (auth('customer')->check()) {
if ($this->prices()->count() > 0) {
$price = $this->prices()->where('customer_level_id', auth()->user()->customer_level_id)->value('price');
return $price;
}
}
return $this->price;
});
}
public function validateDisplayPrice(): Attribute
{
return Attribute::make(get: function () {
if (auth('customer')->check()) {
if ($this->prices()->count() > 0) {
$price = $this->prices()->where('customer_level_id', auth()->user()->customer_level_id)->value('display_price');
return $price;
}
}
return $this->display_price;
});
}
public function location()
{
return $this->belongsTo(Location::class)->withTrashed();
}
public function prices()
{
return $this->hasMany(VoucherPrice::class);
}
public function shuffle_unsold()
{
$voucher = Voucher::where([

@ -8,5 +8,11 @@ class VoucherPrice extends Model
'customer_level_id',
'voucher_id',
'price',
'display_price',
];
public function level()
{
return $this->belongsTo(CustomerLevel::class, 'customer_level_id');
}
}

@ -16,7 +16,8 @@ return new class extends Migration
$table->ulid('customer_level_id')->nullable();
$table->ulid('voucher_id')->nullable();
$table->decimal('price', 20, 2)->nullable();
$table->decimal('price', 20, 2)->default(0);
$table->decimal('display_price', 20, 2)->default(0);
$table->timestamps();
$table->softDeletes();

@ -25,7 +25,7 @@ export default function VoucherCard({ item: { voucher, quantity } }) {
{voucher.profile}
</div>
<div className="text-xl font-bold">
IDR {formatIDR(voucher.price)}
IDR {formatIDR(voucher.validate_price)}
</div>
{+voucher.discount !== 0 && (
<div className="flex flex-row space-x-2 items-center text-xs pb-2">
@ -33,7 +33,7 @@ export default function VoucherCard({ item: { voucher, quantity } }) {
{voucher.discount}%
</div>
<div className="text-gray-400 line-through">
{formatIDR(voucher.display_price)}
{formatIDR(voucher.validate_display_price)}
</div>
</div>
)}
@ -49,10 +49,10 @@ export default function VoucherCard({ item: { voucher, quantity } }) {
</div>
<div className="w-full border border-dashed"></div>
<div className="w-full flex flex-row justify-between items-center pt-1">
<div>{formatIDR(voucher.price)}</div>
<div>{formatIDR(voucher.validate_price)}</div>
<div>x</div>
<div>{quantity}</div>
<div>{formatIDR(+voucher.price * +quantity)}</div>
<div>{formatIDR(+voucher.validate_price * +quantity)}</div>
</div>
<div className="w-full flex flex-row justify-end items-center space-x-2 py-2">
<HiTrash

@ -18,7 +18,7 @@ export default function VoucherCard({ voucher }) {
{voucher.profile}
</div>
<div className="text-xl font-bold">
IDR {formatIDR(voucher.price)}
IDR {formatIDR(voucher.validate_price)}
</div>
{+voucher.discount !== 0 && (
<div className="flex flex-row space-x-2 items-center text-xs pb-2">
@ -26,7 +26,7 @@ export default function VoucherCard({ voucher }) {
{voucher.discount}%
</div>
<div className="text-gray-400 line-through">
{formatIDR(voucher.display_price)}
{formatIDR(voucher.validate_display_price)}
</div>
</div>
)}

@ -4,11 +4,13 @@ import { toast } from 'react-toastify'
export default function VoucherCard(props) {
const {
item: { voucher, quantity, share_word },
item: { share_word, additional_info_json },
} = props
const sale_item = JSON.parse(additional_info_json)
const voucher = sale_item?.voucher
const handleShare = () => {
console.log(share_word)
if (navigator.canShare) {
navigator.share({
title: share_word,
@ -41,7 +43,7 @@ export default function VoucherCard(props) {
{voucher.profile}
</div>
<div className="text-xl font-bold">
IDR {formatIDR(voucher.price)}
IDR {formatIDR(voucher.validate_price)}
</div>
{+voucher.discount !== 0 && (
<div className="flex flex-row space-x-2 items-center text-xs pb-2">
@ -49,7 +51,7 @@ export default function VoucherCard(props) {
{voucher.discount}%
</div>
<div className="text-gray-400 line-through">
{formatIDR(voucher.display_price)}
{formatIDR(voucher.validate_display_price)}
</div>
</div>
)}

@ -63,7 +63,9 @@ export default function Dashboard(props) {
}
const data = {
labels: charts.map((item) => moment(item.date).format('DD MMM YYYY')),
labels: charts.map((item) =>
moment(item.date, 'DD/MM/YYYY').format('DD MMM YYYY')
),
datasets: [
{
label: 'Penjualan',
@ -78,8 +80,8 @@ export default function Dashboard(props) {
router.get(
route(route().current()),
{
startDate: dates.startDate,
endDate: dates.endDate,
start_date: dates.startDate,
end_date: dates.endDate,
customer_id,
location_id,
},

@ -1,4 +1,4 @@
import React, { useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import { isEmpty } from 'lodash'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
@ -7,10 +7,12 @@ import Button from '@/Components/Button'
import { Head, useForm } from '@inertiajs/react'
import FormInputWith from '@/Components/FormInputWith'
import LocationSelectionInput from '../Location/SelectionInput'
import Checkbox from '@/Components/Checkbox'
export default function Form(props) {
const { voucher } = props
const { voucher, levels } = props
const [use_level, setUseLevel] = useState(false)
const { data, setData, post, processing, errors } = useForm({
username: '',
password: '',
@ -22,6 +24,7 @@ export default function Form(props) {
expired: '',
expired_unit: 'Hari',
location_id: null,
prices: null,
})
const handleOnChange = (event) => {
@ -34,6 +37,38 @@ export default function Form(props) {
: event.target.value
)
}
const handleUseLevel = () => {
setUseLevel(!use_level)
if (!use_level === true) {
const prices = levels.map((level) => {
return {
name: level.name,
customer_level_id: level.id,
display_price: '0',
}
})
setData('prices', prices)
return
}
setData('prices', null)
}
const handleSetLevelPrice = (id, value) => {
setData(
'prices',
data.prices.map((price) => {
if (price.customer_level_id === id) {
return {
...price,
display_price: value,
}
}
return price
})
)
}
const handleSubmit = () => {
if (isEmpty(voucher) === false) {
post(route('voucher.update', voucher))
@ -44,6 +79,17 @@ export default function Form(props) {
useEffect(() => {
if (isEmpty(voucher) === false) {
let prices = null
if (voucher.prices.length > 0) {
setUseLevel(true)
prices = voucher.prices.map((price) => {
return {
...price,
name: price.level.name,
}
})
}
setData({
username: voucher.username,
password: voucher.password,
@ -55,6 +101,7 @@ export default function Form(props) {
expired: voucher.expired,
expired_unit: voucher.expired_unit,
location_id: voucher.location_id,
prices: prices,
})
}
}, [voucher])
@ -136,7 +183,7 @@ export default function Form(props) {
label="Comment"
error={errors.comment}
/>
<div>
<div className="mb-2">
<label className="block text-sm font-medium text-gray-900 dark:text-white">
Masa Aktif
</label>
@ -166,6 +213,36 @@ export default function Form(props) {
</div>
</div>
</div>
<Checkbox
label="Level Harga"
value={use_level}
onChange={(e) => handleUseLevel(e.target.value)}
/>
<div
className={`p-2 mt-2 border rounded ${
!use_level && 'invisible'
}`}
>
{data.prices?.map((price) => (
<FormInput
type="number"
key={price.customer_level_id}
value={price.display_price}
onChange={(e) =>
handleSetLevelPrice(
price.customer_level_id,
e.target.value
)
}
label={price.name}
/>
))}
{errors.prices && (
<p className="mb-2 text-sm text-red-600 dark:text-red-500">
{errors.prices}
</p>
)}
</div>
<div className="mt-8">
<Button
onClick={handleSubmit}

@ -1,4 +1,4 @@
import React, { useEffect } from 'react'
import React, { useState } from 'react'
import { isEmpty } from 'lodash'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
@ -8,8 +8,11 @@ import { Head, useForm } from '@inertiajs/react'
import FormInputWith from '@/Components/FormInputWith'
import LocationSelectionInput from '../Location/SelectionInput'
import TextArea from '@/Components/TextArea'
import Checkbox from '@/Components/Checkbox'
export default function Import(props) {
const { levels } = props
const [use_level, setUseLevel] = useState(false)
const { data, setData, post, processing, errors } = useForm({
script: '',
discount: 0,
@ -17,8 +20,40 @@ export default function Import(props) {
expired: '',
expired_unit: 'Hari',
location_id: null,
prices: null,
})
const handleUseLevel = () => {
setUseLevel(!use_level)
if (!use_level === true) {
const prices = levels.map((level) => {
return {
name: level.name,
customer_level_id: level.id,
display_price: '0',
}
})
setData('prices', prices)
return
}
setData('prices', null)
}
const handleSetLevelPrice = (id, value) => {
setData(
'prices',
data.prices.map((price) => {
if (price.customer_level_id === id) {
return {
...price,
display_price: value,
}
}
return price
})
)
}
const handleOnChange = (event) => {
setData(
event.target.name,
@ -104,6 +139,36 @@ export default function Import(props) {
</div>
</div>
</div>
<Checkbox
label="Level Harga"
value={use_level}
onChange={(e) => handleUseLevel(e.target.value)}
/>
<div
className={`p-2 my-2 border rounded ${
!use_level && 'invisible'
}`}
>
{data.prices?.map((price) => (
<FormInput
type="number"
key={price.customer_level_id}
value={price.display_price}
onChange={(e) =>
handleSetLevelPrice(
price.customer_level_id,
e.target.value
)
}
label={price.name}
/>
))}
{errors.prices && (
<p className="mb-2 text-sm text-red-600 dark:text-red-500">
{errors.prices}
</p>
)}
</div>
<TextArea
name="script"
value={data.script}

Loading…
Cancel
Save