Compare commits

..

47 Commits

Author SHA1 Message Date
bc6ae43121 update elusive price
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 4m55s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 42s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-18 10:48:12 +07:00
8307b9e66d finishing to dev
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m41s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 42s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-14 22:50:44 +07:00
2fabdf8fc9 update perbaikan pos, return, dan report profit nlost
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 4m17s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 15s
Build, Push and Deploy / deploy-staging (push) Successful in 41s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-14 22:19:01 +07:00
1dfc06635d update wuerry
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m59s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 17s
Build, Push and Deploy / deploy-staging (push) Successful in 41s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-14 13:29:43 +07:00
6f9bdff115 update
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m59s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 18s
Build, Push and Deploy / deploy-staging (push) Successful in 39s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-14 12:25:21 +07:00
ce2b815b07 Fix BadMethodCallException and remaining PostgreSQL issues 2026-05-14 12:24:33 +07:00
b76d34cc64 update
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 4m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 17s
Build, Push and Deploy / deploy-staging (push) Successful in 39s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-14 12:12:26 +07:00
05fd3230b8 update marketing
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 5m14s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 17s
Build, Push and Deploy / deploy-staging (push) Successful in 41s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-14 11:55:22 +07:00
f80fcc4c1b cari error git
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 4m22s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 17s
Build, Push and Deploy / deploy-staging (push) Successful in 42s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-12 08:28:50 +07:00
b61a076fa6 cek log error
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 4m40s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 42s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-11 11:16:47 +07:00
8a567280ae update perbaikan email
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m51s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 38s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-09 15:24:19 +07:00
145bd5675f update mail
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m48s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 15s
Build, Push and Deploy / deploy-staging (push) Successful in 39s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-09 15:09:09 +07:00
6e076e8f55 update mail flow
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 4m32s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 41s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-09 14:50:35 +07:00
0cb58d3419 update error
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 4m50s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 41s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-06 09:49:01 +07:00
28510f1163 update docker ignore
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m47s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 38s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-05 18:07:28 +07:00
f301d311d6 update date
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m33s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 17s
Build, Push and Deploy / deploy-staging (push) Successful in 33s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-05 17:54:11 +07:00
353eec5e74 update git ignore
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m29s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 17s
Build, Push and Deploy / deploy-staging (push) Successful in 35s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-05 17:42:43 +07:00
e49654a1f7 update compose
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m30s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 17s
Build, Push and Deploy / deploy-staging (push) Successful in 35s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-05 17:33:22 +07:00
3b3a1da5c3 update migration
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m27s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 17s
Build, Push and Deploy / deploy-staging (push) Successful in 23s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-05 17:18:42 +07:00
30de18d283 update big migrate
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 4m17s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 20s
Build, Push and Deploy / deploy-staging (push) Successful in 35s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-05 15:29:32 +07:00
e34027132e update
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m50s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 17s
Build, Push and Deploy / deploy-staging (push) Successful in 33s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-04 23:30:59 +07:00
1dde763a73 solving error
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m55s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 15s
Build, Push and Deploy / deploy-staging (push) Successful in 40s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-04 23:13:25 +07:00
3e1b64e008 update addon HRM
Some checks failed
Build, Push and Deploy / build-and-push (push) Failing after 3m30s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 15s
Build, Push and Deploy / deploy-staging (push) Has been skipped
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-05-04 23:04:09 +07:00
7be161f4e4 resolving
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 5m14s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 17s
Build, Push and Deploy / deploy-staging (push) Successful in 41s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-27 08:58:08 +07:00
7beac673c8 pull from web 2026-04-27 08:56:13 +07:00
triagungbiantoro
417b7b7453 harga pada beranda terhubung ke manage plans di dashboard admin
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m27s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 31s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-25 21:57:25 +07:00
f25542ea0d update grafik revenuw di dashboard
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m58s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 42s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-23 22:28:02 +07:00
f2cd16a2a8 update querry report profit n lost
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m48s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 15s
Build, Push and Deploy / deploy-staging (push) Successful in 40s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-23 22:17:41 +07:00
4f530ff301 perbaikan product string 1 bugs
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m51s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 15s
Build, Push and Deploy / deploy-staging (push) Successful in 41s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-18 23:08:39 +07:00
6319d4c6cb update on psosale string 1 bugs
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m15s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 14s
Build, Push and Deploy / deploy-staging (push) Successful in 36s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-18 22:47:24 +07:00
2173c3ee81 update gitignore composer.js
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m23s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 14s
Build, Push and Deploy / deploy-staging (push) Successful in 37s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-18 21:41:31 +07:00
dcaf267458 update lock clucknut
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 14s
Build, Push and Deploy / build-and-push (push) Successful in 3m14s
Build, Push and Deploy / deploy-staging (push) Successful in 25s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-18 20:32:18 +07:00
4554035227 fix: enforce php 8.2.30 platform for docker compatibility
Some checks failed
Build, Push and Deploy / deploy-staging (push) Has been cancelled
Build, Push and Deploy / deploy-production (push) Has been cancelled
Build, Push and Deploy / build-and-push (push) Has been cancelled
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
2026-04-18 20:29:54 +07:00
f459f03d0c perbaikan bug dashboard
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m20s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 14s
Build, Push and Deploy / deploy-staging (push) Successful in 26s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-18 19:55:00 +07:00
ef902b2604 update bug daashboards 2026-04-18 19:47:13 +07:00
dd60899d9e updater
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m29s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 14s
Build, Push and Deploy / deploy-staging (push) Successful in 26s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-18 19:07:27 +07:00
1e252a0e4c update git pus
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m35s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 14s
Build, Push and Deploy / deploy-staging (push) Successful in 30s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-18 16:22:32 +07:00
ed9654271b fix: sync composer.lock with composer.json after adding scribe and missing extensions 2026-04-18 16:21:59 +07:00
1228c52538 update after stop build
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m36s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 42s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-18 16:10:57 +07:00
e449146306 update dockerfile
Some checks failed
Build, Push and Deploy / build-and-push (push) Failing after 2m15s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 15s
Build, Push and Deploy / deploy-staging (push) Has been skipped
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-18 16:02:16 +07:00
62417c44be Update bunines bugs
Some checks failed
Build, Push and Deploy / build-and-push (push) Failing after 2m57s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Has been skipped
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-18 15:50:39 +07:00
e2fa4d2d7b create insert db querry
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m31s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 15s
Build, Push and Deploy / deploy-staging (push) Successful in 37s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-16 05:04:06 +07:00
75dfca9128 update
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m17s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 15s
Build, Push and Deploy / deploy-staging (push) Successful in 37s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-15 22:26:07 +07:00
58edd89bd1 refressh database
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 4m1s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 35s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-15 20:34:37 +07:00
de510a05f1 seeder db
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m46s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 18s
Build, Push and Deploy / deploy-staging (push) Successful in 45s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-13 09:24:39 +07:00
4e58905bf7 migrate
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 15s
Build, Push and Deploy / build-and-push (push) Successful in 3m0s
Build, Push and Deploy / deploy-staging (push) Successful in 36s
Build, Push and Deploy / deploy-production (push) Has been skipped
2026-04-12 23:49:02 +07:00
2e2b0ee178 test migrate 2026-04-12 23:46:56 +07:00
22358 changed files with 271173 additions and 79543 deletions

View File

@@ -27,4 +27,4 @@
storage/framework/sessions/*
storage/framework/views/*
storage/framework/cache/*
bootstrap/cache/*
bootstrap/cache/*

5
.gitignore vendored
View File

@@ -1,7 +1,7 @@
/node_modules
/public/build
/public/hot
#/vendor
/vendor
.env
.env.backup
.phpunit.result.cache
@@ -20,4 +20,5 @@ yarn-error.log
/public/modules/*
.qodo
.camel-jbang
dump/
dump/
db/

0
.scannerwork/.sonar_lock Normal file
View File

View File

@@ -0,0 +1,6 @@
projectKey=kulak
serverUrl=http://localhost:9000
serverVersion=26.4.0.121862
dashboardUrl=http://localhost:9000/dashboard?id=kulak
ceTaskId=77035e85-cf5d-4771-ab8e-51da185429c5
ceTaskUrl=http://localhost:9000/api/ce/task?id=77035e85-cf5d-4771-ab8e-51da185429c5

4
.scribe/.filehashes Normal file
View File

@@ -0,0 +1,4 @@
# GENERATED. YOU SHOULDN'T MODIFY OR DELETE THIS FILE.
# Scribe uses this file to know when you change something manually in your docs.
.scribe/intro.md=4cce32d41499a35efb2ff125bcc4d59c
.scribe/auth.md=9bee2b1ef8a238b2e58613fa636d5f39

3
.scribe/auth.md Normal file
View File

@@ -0,0 +1,3 @@
# Authenticating requests
This API is not authenticated.

File diff suppressed because it is too large Load Diff

10920
.scribe/endpoints/00.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
# To include an endpoint that isn't a part of your Laravel app (or belongs to a vendor package),
# you can define it in a custom.*.yaml file, like this one.
# Each custom file should contain an array of endpoints. Here's an example:
# See https://scribe.knuckles.wtf/laravel/documenting/custom-endpoints#extra-sorting-groups-in-custom-endpoint-files for more options
#- httpMethods:
# - POST
# uri: api/doSomething/{param}
# metadata:
# groupName: The group the endpoint belongs to. Can be a new group or an existing group.
# groupDescription: A description for the group. You don't need to set this for every endpoint; once is enough.
# subgroup: You can add a subgroup, too.
# title: Do something
# description: 'This endpoint allows you to do something.'
# authenticated: false
# headers:
# Content-Type: application/json
# Accept: application/json
# urlParameters:
# param:
# name: param
# description: A URL param for no reason.
# required: true
# example: 2
# type: integer
# queryParameters:
# speed:
# name: speed
# description: How fast the thing should be done. Can be `slow` or `fast`.
# required: false
# example: fast
# type: string
# bodyParameters:
# something:
# name: something
# description: The things we should do.
# required: true
# example:
# - string 1
# - string 2
# type: 'string[]'
# responses:
# - status: 200
# description: 'When the thing was done smoothly.'
# content: # Your response content can be an object, an array, a string or empty.
# {
# "hey": "ho ho ho"
# }
# responseFields:
# hey:
# name: hey
# description: Who knows?
# type: string # This is optional

13
.scribe/intro.md Normal file
View File

@@ -0,0 +1,13 @@
# Introduction
<aside>
<strong>Base URL</strong>: <code>http://localhost:8080</code>
</aside>
This documentation aims to provide all the information you need to work with our API.
<aside>As you scroll, you'll see code examples for working with the API in different programming languages in the dark area to the right (or as part of the content on mobile).
You can switch the language used with the tabs at the top right (or from the nav menu at the top left on mobile).</aside>

View File

@@ -9,9 +9,12 @@ RUN apk add --no-cache \
libpng-dev \
libjpeg-turbo-dev \
freetype-dev \
libxml2-dev \
icu-dev \
oniguruma-dev \
git \
curl \
postgresql-client \
nodejs \
npm \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
@@ -23,7 +26,10 @@ RUN apk add --no-cache \
bcmath \
gd \
exif \
pcntl
pcntl \
xml \
dom \
intl
# Configure git to allow safe directory
RUN git config --global --add safe.directory /var/www/html
@@ -34,7 +40,7 @@ COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
# Copy ONLY composer files first
COPY composer.json ./
COPY composer.json composer.lock ./
# Create storage and bootstrap directories needed for composer scripts
RUN mkdir -p storage/framework/cache \
@@ -53,9 +59,9 @@ ENV COMPOSER_ALLOW_SUPERUSER=1 \
# Install PHP dependencies (this layer will be cached)
RUN composer config -g repo.packagist composer https://packagist.org \
&& composer install --no-interaction --no-progress --no-scripts --prefer-dist
&& composer install --no-interaction --no-progress --no-scripts --prefer-dist --ignore-platform-reqs
# Isolate the clean vendor directory
# Isolate the clean vendor directory
#RUN mv vendor /vendor_clean
# Copy the rest of the application files (will ignore vendor because of .dockerignore)

Binary file not shown.

View File

@@ -4,36 +4,42 @@
use App\Models\PaymentType;
use App\Models\Product;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportBalanceSheet implements FromView
{
use DateFilterTrait;
public function view(): View
{
$businessId = auth()->user()->business_id;
$duration = request('custom_days') ? : 'today';
$fromDate = request('from_date');
$toDate = request('to_date');
$perPage = request('per_page') ?? 20;
// PRODUCT
$productQuery = Product::select('id', 'business_id', 'productName', 'product_type', 'created_at')
->with(['stocks:id,business_id,product_id,productStock,productPurchasePrice', 'combo_products.stock'])
->where('business_id', $businessId);
$this->applyDateFilter($productQuery, $duration, 'created_at', $fromDate, $toDate);
$products = $productQuery->get()
->map(function ($item) {
$item->source = 'product';
return $item;
});
// BANK
$bankQuery = PaymentType::where('business_id', $businessId);
$this->applyDateFilter($bankQuery, $duration, 'opening_date', $fromDate, $toDate);
$banks = $bankQuery->get()
->map(function ($item) {
$item->source = 'bank';
return $item;
});
$asset_datas = $products->merge($banks);
$asset_datas = $products->merge($banks)->take($perPage);
// TOTAL STOCK VALUE
$total_stock_value = 0;
foreach ($products as $product) {
if (in_array($product->product_type, ['single', 'variant'])) {

View File

@@ -3,17 +3,42 @@
namespace Modules\Business\App\Exports;
use App\Models\Sale;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportBillWisePofit implements FromView
{
use DateFilterTrait;
public function view(): View
{
$profits = Sale::with('party:id,name')
->where('business_id', auth()->user()->business_id)
->latest()
->get();
$salesQuery = Sale::with('party:id,name')
->where('business_id', auth()->user()->business_id);
$salesQuery->when(request('search'), function ($query) {
$query->where(function ($q) {
$q->where('lossProfit', 'like', '%' . request('search') . '%')
->orWhere('totalAmount', 'like', '%' . request('search') . '%')
->orWhere('invoiceNumber', 'like', '%' . request('search') . '%')
->orWhereHas('party', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
});
});
$salesQuery->when(request('party_id'), function ($query, $partyId) {
if ($partyId === 'Guest') {
$query->whereNull('party_id');
} else {
$query->where('party_id', $partyId);
}
});
$duration = request('custom_days') ?: 'today';
$this->applyDateFilter($salesQuery, $duration, 'saleDate', request('from_date'), request('to_date'));
$profits = $salesQuery->latest()->limit(request('per_page') ?? 20)->get();
return view('business::bill-wise-profits.excel-csv', compact('profits'));
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Modules\Business\App\Exports;
use App\Models\PaymentType;
use App\Models\Product;
use App\Models\Transaction;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportCashBalance implements FromView
{
use DateFilterTrait;
public function view(): View
{
$cash = Transaction::with('user:id,name')
->where('business_id', auth()->user()->business_id)
->whereIn('transaction_type', ['cash_payment', 'bank_to_cash', 'cash_to_bank', 'adjust_cash', 'cheque_to_cash']);
// Date Filter
$duration = request('custom_days') ?: 'today';
$this->applyDateFilter($cash, $duration, 'date', request('from_date'), request('to_date'));
$cashes = $cash->when(request('search'), function ($q) {
$q->where(function ($q) {
$q->where('date', 'like', '%' . request('search') . '%')
->orWhere('platform', 'like', '%' . request('search'). '%')
->orWhere('transaction_type', 'like', '%' . request('search') . '%')
->orWhere('amount', 'like', '%' . request('search') . '%')
->orWhereHas('user', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
});
})
->latest()
->limit(request('per_page') ?? 20)->get();
return view('business::cashes.excel-csv', compact('cashes'));
}
}

View File

@@ -3,14 +3,17 @@
namespace Modules\Business\App\Exports;
use App\Models\Transaction;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportCashFlowReport implements FromView
{
use DateFilterTrait;
public function view(): View
{
$cash_flows = Transaction::with([
$query = Transaction::with([
'paymentType:id,name',
'sale:id,party_id',
'sale.party:id,name',
@@ -22,11 +25,44 @@ public function view(): View
'dueCollect.party:id,name',
])
->where('business_id', auth()->user()->business_id)
->whereIn('type', ['debit', 'credit'])
->latest()
->get();
->whereIn('type', ['debit', 'credit']);
$opening_balance = 0;
// Date Filter
$duration = request('custom_days') ?: 'today';
$this->applyDateFilter($query, $duration, 'date', request('from_date'), request('to_date'));
// Search filter
if (request('search')) {
$query->where(function ($query) {
$query->where('date', 'like', '%' . request('search') . '%')
->orWhere('invoice_no', 'like', '%' . request('search') . '%')
->orWhere('platform', 'like', '%' . request('search') . '%')
->orWhere('amount', 'like', '%' . request('search') . '%')
->orWhere('transaction_type', 'like', '%' . request('search') . '%')
->orWhereHas('paymentType', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
});
}
// Platform filter
if (request('platform')) {
$query->where('platform', request('platform'));
}
// Paginate data
$cash_flows = $query->limit(request('per_page') ?? 20)->get();
$firstDate = $cash_flows->first()?->date;
if ($firstDate) {
$opening_balance = (clone $query)
->whereDate('date', '<', $firstDate)
->selectRaw("SUM(CASE WHEN type='credit' THEN amount ELSE 0 END) - SUM(CASE WHEN type='debit' THEN amount ELSE 0 END) as balance")
->value('balance') ?? 0;
} else {
$opening_balance = 0;
}
return view('business::cash-flow.excel-csv', compact('cash_flows', 'opening_balance'));
}

View File

@@ -11,12 +11,39 @@ class ExportComboProduct implements FromView
public function view(): View
{
$combo_products = Product::with(['combo_products', 'unit:id,unitName', 'category:id,categoryName'])
$user = auth()->user();
$branchId = null;
if (moduleCheck('MultiBranchAddon')) {
$branchId = $user->branch_id ?? $user->active_branch_id;
}
$search = request('search');
$combo_products = Product::with(['combo_products.stock.product', 'unit:id,unitName', 'category:id,categoryName'])
->where('business_id', auth()->user()->business_id)
->where('product_type', 'combo')
->withCount('combo_products as total_combo_products')
->when($search, function ($q) use ($search) {
$q->where(function ($q) use ($search) {
$q->where('productName', 'like', '%' . $search . '%')
->orWhere('productCode', 'like', '%' . $search . '%')
->orWhereHas('category', function ($q) use ($search) {
$q->where('categoryName', 'like', '%' . $search . '%');
})
->orWhereHas('unit', function ($q) use ($search) {
$q->where('unitName', 'like', '%' . $search . '%');
});
});
})
->when($branchId, function ($q) use ($branchId) {
$q->whereHas('combo_products.stock', function ($q) use ($branchId) {
$q->where('branch_id', $branchId);
});
})
->latest()
->get();
->limit(request('per_page') ?? 20)->get();
$combo_products->transform(function ($product) {
$product->total_stock = $product->combo_products->sum(function ($combo) {
@@ -29,7 +56,7 @@ public function view(): View
return $product;
});
return view('business::products.combo-products.excel-csv', [
'combo_products' => $combo_products
]);

View File

@@ -11,12 +11,39 @@ class ExportComboProductReport implements FromView
public function view(): View
{
$combo_products = Product::with(['combo_products', 'unit:id,unitName', 'category:id,categoryName'])
$user = auth()->user();
$branchId = null;
if (moduleCheck('MultiBranchAddon')) {
$branchId = $user->branch_id ?? $user->active_branch_id;
}
$search = request('search');
$combo_products = Product::with(['combo_products.stock.product', 'unit:id,unitName', 'category:id,categoryName'])
->where('business_id', auth()->user()->business_id)
->where('product_type', 'combo')
->withCount('combo_products as total_combo_products')
->when($search, function ($q) use ($search) {
$q->where(function ($q) use ($search) {
$q->where('productName', 'like', '%' . $search . '%')
->orWhere('productCode', 'like', '%' . $search . '%')
->orWhereHas('category', function ($q) use ($search) {
$q->where('categoryName', 'like', '%' . $search . '%');
})
->orWhereHas('unit', function ($q) use ($search) {
$q->where('unitName', 'like', '%' . $search . '%');
});
});
})
->when($branchId, function ($q) use ($branchId) {
$q->whereHas('combo_products.stock', function ($q) use ($branchId) {
$q->where('branch_id', $branchId);
});
})
->latest()
->get();
->limit(request('per_page') ?? 20)->get();
$combo_products->transform(function ($product) {
$product->total_stock = $product->combo_products->sum(function ($combo) {

View File

@@ -10,21 +10,58 @@ class ExportCurrentStock implements FromView
{
public function view(): View
{
$query = Product::with('stocks')
->where('product_type', '!=', 'combo')
->where('business_id', auth()->user()->business_id);
$businessId = auth()->user()->business_id;
$alert_qty_filter = request('alert_qty');
$search_term = request('search');
$per_page = request('per_page', 20);
if (request('alert_qty')) {
$products = $query->get()->filter(function ($product) {
$totalStock = $product->stocks->sum('productStock');
return $totalStock <= $product->alert_qty;
// Base query
$query = Product::with('stocks', 'category:id,categoryName')
->where('product_type', '!=', 'combo')
->where('business_id', $businessId);
if ($search_term) {
$query->where(function ($q) use ($search_term) {
$q->where('productName', 'like', '%' . $search_term . '%')
->orWhere('productCode', 'like', '%' . $search_term . '%')
->orWhere('hsn_code', 'like', '%' . $search_term . '%')
->orWhereHas('category', function ($q) use ($search_term) {
$q->where('categoryName', 'like', '%' . $search_term . '%');
});
});
} else {
$products = $query->latest()->get();
}
return view('business::stocks.excel-csv', [
'products' => $products
]);
if ($alert_qty_filter) {
$products = $query
->withSum('stocks as total_stock', 'productStock')
->havingRaw('total_stock <= alert_qty')
->latest()
->limit($per_page)
->get();
$total_stock_qty = $products->sum('total_stock');
$total_stock_value = $products->sum(function ($product) {
return $product->stocks->sum(function ($stock) {
return $stock->productStock * $stock->productPurchasePrice;
});
});
} else {
$products = $query
->withSum('stocks as total_stock', 'productStock')
->latest()
->limit($per_page)
->get();
$total_stock_qty = $products->sum('total_stock');
$total_stock_value = $products->sum(function ($product) {
return $product->stocks->sum(function ($stock) {
return $stock->productStock * $stock->productPurchasePrice;
});
});
}
return view('business::stocks.excel-csv', compact('products', 'total_stock_qty', 'total_stock_value'));
}
}

View File

@@ -10,13 +10,27 @@ class ExportCurrentStockReport implements FromView
{
public function view(): View
{
return view('business::reports.stocks.excel-csv', [
'stock_reports' => Product::with('stocks')
->where('business_id', auth()->user()->business_id)
->where('product_type', '!=', 'combo')
->withSum('stocks', 'productStock')
->latest()
->get()
]);
$productsQuery = Product::with(['stocks' => function ($q) {
$q->select('id', 'product_id', 'productStock', 'productPurchasePrice', 'productSalePrice');
}])
->where('business_id', auth()->user()->business_id)
->where('product_type', '!=', 'combo');
if (request('search')) {
$productsQuery->where(function ($q) {
$q->where('productName', 'like', '%' . request('search') . '%')
->orWhereHas('stocks', function ($stock) {
$stock->where('productSalePrice', 'like', '%' . request('search') . '%')
->orWhere('productPurchasePrice', 'like', '%' . request('search') . '%');
});
});
}
$stock_reports = $productsQuery
->latest()
->limit($request->per_page ?? 20)
->get();
return view('business::reports.stocks.excel-csv', compact('stock_reports'));
}
}

View File

@@ -13,8 +13,20 @@ public function view(): View
$customers = Party::with('sales')
->where('business_id', auth()->user()->business_id)
->where('type', '!=', 'Supplier')
->when(request('search'), function ($q) {
$q->where(function ($q) {
$q->where('name', 'like', '%' . request('search') . '%')
->orWhere('phone', 'like', '%' . request('search') . '%')
->orWhere('type', 'like', '%' . request('search') . '%');
});
})
->when(request('type'), function ($q) {
$q->where(function ($q) {
$q->where('type', request('type'));
});
})
->latest()
->get();
->limit(request('per_page') ?? 20)->get();
$totalAmount = $customers->sum(function ($customer) {
return $customer->sales?->sum('totalAmount') ?? 0;

View File

@@ -3,14 +3,17 @@
namespace Modules\Business\App\Exports;
use App\Models\Transaction;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportDayBookReport implements FromView
{
use DateFilterTrait;
public function view(): View
{
$day_books = Transaction::with([
$query = Transaction::with([
'paymentType:id,name',
'sale:id,user_id,party_id,totalAmount',
'sale.party:id,name',
@@ -25,9 +28,34 @@ public function view(): View
'dueCollect.user:id,name',
])
->where('business_id', auth()->user()->business_id)
->whereIn('type', ['debit', 'credit'])
->latest()
->get();
->whereIn('type', ['debit', 'credit']);
// Apply date filter
$duration = request('custom_days') ?: 'today';
$this->applyDateFilter($query, $duration, 'date', request('from_date'), request('to_date'));
// Search filter
if (request('search')) {
$query->where(function ($query) {
$query->where('date', 'like', '%' . request('search') . '%')
->orWhere('invoice_no', 'like', '%' . request('search') . '%')
->orWhere('platform', 'like', '%' . request('search') . '%')
->orWhere('amount', 'like', '%' . request('search') . '%')
->orWhere('type', 'like', '%' . request('search') . '%')
->orWhere('transaction_type', 'like', '%' . request('search') . '%')
->orWhereHas('paymentType', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
});
}
// Platform filter
if (request('platform')) {
$query->where('platform', request('platform'));
}
// Paginate data
$day_books = $query->latest()->limit(request('per_page') ?? 20)->get();
return view('business::day-book.excel-csv', compact('day_books'));
}

View File

@@ -10,11 +10,33 @@ class ExportDiscountProduct implements FromView
{
public function view(): View
{
$discount_products = SaleDetails::with('product:id,productName,productCode')
$user = auth()->user();
$branchId = null;
if (moduleCheck('MultiBranchAddon')) {
$branchId = $user->branch_id ?? $user->active_branch_id;
}
$discount_products = SaleDetails::with('product:id,productName,productCode,hsn_code')
->whereHas('product', function ($q) {
$q->where('business_id', auth()->user()->business_id);
})
->when(request('search'), function ($q) {
$q->whereHas('product', function ($q) {
$q->where('productName', 'like', '%' . request('search') . '%')
->orWhere('productCode', 'like', '%' . request('search') . '%')
->orWhere('productPurchasePrice', 'like', '%' . request('search') . '%')
->orWhere('price', 'like', '%' . request('search') . '%')
->orWhere('discount', 'like', '%' . request('search') . '%');
});
})
->when($branchId, function ($q) use ($branchId) {
$q->whereHas('sale', function ($q) use ($branchId) {
$q->where('branch_id', $branchId);
});
})
->where('discount', '>', 0)
->limit(request('per_page') ?? 20)
->get();
return view('business::reports.discount-products.excel-csv', compact('discount_products'));

View File

@@ -11,37 +11,56 @@ class ExportDue implements FromView
public function view(): View
{
$user = auth()->user();
$businessId = $user->business_id;
$activeBranch = $user->active_branch;
$business_id = $user->business_id;
$query = Party::where('business_id', $businessId)
$query = Party::where('business_id', $business_id)
->where('type', '!=', 'Supplier')
->when(request('search'), function ($q) {
$q->where(function ($q2) {
$q2->where('type', 'like', '%' . request('search') . '%')
->orWhere('name', 'like', '%' . request('search') . '%')
->orWhere('phone', 'like', '%' . request('search') . '%')
->orWhere('credit_limit', 'like', '%' . request('search') . '%');
});
})
->when(request('type'), function ($q) {
$q->where('type', request('type'));
})
->with('sales_dues')
->latest();
if ($activeBranch) {
$query->whereHas('sales_dues', function ($q) use ($activeBranch) {
$q->where('branch_id', $activeBranch->id)
->where('dueAmount', '>', 0);
});
} else {
$query->where('due', '>', 0);
}
$parties = $query->get();
if ($activeBranch) {
$parties->transform(function ($supplier) use ($activeBranch) {
$supplier->due = $supplier->sales_dues
->where('branch_id', $activeBranch->id)
->sum('dueAmount');
return $supplier;
})->filter(fn($supplier) => $supplier->due > 0);
$parties = $parties
->transform(function ($customer) use ($activeBranch) {
$party_due = $customer->sales_dues
->where('branch_id', $activeBranch->id)
->sum('dueAmount');
if ($customer->branch_id === $activeBranch->id) {
$openingBalanceDue = $customer->opening_balance_type === 'due'
? ($customer->opening_balance ?? 0)
: 0;
$customer->due = $openingBalanceDue + $party_due;
} else {
$customer->due = $party_due;
}
return $customer;
})
->filter(fn($customer) => $customer->due > 0)
->take(request('per_page') ?? 20)
->values();
} else {
$parties = $parties
->filter(fn($customer) => ($customer->due ?? 0) > 0)
->take(request('per_page') ?? 20)
->values();
}
return view('business::reports.due.excel-csv', [
'parties' => $parties
]);
return view('business::reports.due.excel-csv', compact('parties'));
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Modules\Business\App\Exports;
use App\Models\Party;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportDueList implements FromView
{
public function view(): View
{
$businessId = auth()->user()->business_id;
$activeBranch = auth()->user()->active_branch;
$query = Party::where('business_id', $businessId)
->with(['purchases_dues', 'sales_dues']);
if (request('type')) {
$query->where('type', request('type'));
}
if (request('search')) {
$search = request('search');
$query->where(function ($q) use ($search, $activeBranch) {
$q->where('type', 'like', "%$search%")
->orWhere('name', 'like', "%$search%")
->orWhere('phone', 'like', "%$search%")
->orWhere('email', 'like', "%$search%");
if (!$activeBranch) {
$q->orWhere('due', 'like', "%$search%");
}
});
}
$parties = $query->latest()->limit(request('per_page') ?? 20)->get();
if ($activeBranch) {
$parties = $parties->map(function ($party) use ($activeBranch) {
$sale_dues = $party->type === 'Supplier'
? $party->purchases_dues->where('branch_id', $activeBranch->id)
: $party->sales_dues->where('branch_id', $activeBranch->id);
$party_due = $sale_dues->sum('dueAmount');
if ($party->branch_id === $activeBranch->id) {
$openingBalanceDue = $party->opening_balance_type === 'due' ? ($party->opening_balance ?? 0) : 0;
$party->due = $openingBalanceDue + $party_due;
} else {
$party->due = $party_due;
}
return $party;
})->filter(fn($p) => $p->due > 0)->values();
} else {
$parties = $parties->filter(fn($p) => $p->due > 0)->values();
}
return view('business::dues.excel-csv', [
'parties' => $parties
]);
}
}

View File

@@ -3,15 +3,48 @@
namespace Modules\Business\App\Exports;
use App\Models\Expense;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportExpense implements FromView
{
use DateFilterTrait;
public function view(): View
{
return view('business::reports.expense.excel-csv', [
'expense_reports' => Expense::with('category:id,categoryName', 'payment_type:id,name', 'branch:id,name', 'transactions')->where('business_id', auth()->user()->business_id)->latest()->get()
]);
$expenseQuery = Expense::with('category:id,categoryName', 'payment_type:id,name', 'branch:id,name', 'transactions')
->where('business_id', auth()->user()->business_id);
$expenseQuery->when(request('branch_id'), function ($q) {
$q->where('branch_id', request('branch_id'));
});
// Date Filter
$duration = request('custom_days') ?: 'today';
$this->applyDateFilter($expenseQuery, $duration, 'expenseDate', request('from_date'), request('to_date'));
// Search Filter
if (request('search')) {
$expenseQuery->where(function ($query) {
$query->where('expanseFor', 'like', '%' . request('search') . '%')
->orWhere('paymentType', 'like', '%' . request('search') . '%')
->orWhere('referenceNo', 'like', '%' . request('search') . '%')
->orWhere('amount', 'like', '%' . request('search') . '%')
->orWhereHas('category', function ($q) {
$q->where('categoryName', 'like', '%' . request('search') . '%');
})
->orWhereHas('payment_type', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
})
->orWhereHas('branch', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
});
}
$expense_reports = $expenseQuery->latest()->limit(request('per_page' ?? 20))->get();
return view('business::reports.expense.excel-csv', compact('expense_reports'));
}
}

View File

@@ -10,16 +10,35 @@ class ExportExpiredProduct implements FromView
{
public function view(): View
{
$expired_products = Product::with('stocks', 'unit:id,unitName', 'brand:id,brandName', 'category:id,categoryName')
$expired_products = Product::with('unit:id,unitName', 'brand:id,brandName', 'category:id,categoryName', 'stocks')
->where('business_id', auth()->user()->business_id)
->withSum('stocks as total_stock', 'productStock')
->whereHas('stocks', function ($query) {
$query->whereDate('expire_date', '<', today())->where('productStock', '>', 0);
})
->latest()->get();
$query->whereDate('expire_date', '<', today())
->where('productStock', '>', 0);
})
->when(request('search'), function ($q) {
$q->where(function ($q) {
$q->where('type', 'like', '%' . request('search') . '%')
->orWhere('productName', 'like', '%' . request('search') . '%')
->orWhere('productCode', 'like', '%' . request('search') . '%')
->orWhere('hsn_code', 'like', '%' . request('search') . '%')
->orWhere('productSalePrice', 'like', '%' . request('search') . '%')
->orWhere('productPurchasePrice', 'like', '%' . request('search') . '%')
->orWhereHas('unit', function ($q) {
$q->where('unitName', 'like', '%' . request('search') . '%');
})
->orWhereHas('brand', function ($q) {
$q->where('brandName', 'like', '%' . request('search') . '%');
})
->orWhereHas('category', function ($q) {
$q->where('categoryName', 'like', '%' . request('search') . '%');
});
});
})
->latest()
->limit(request('per_page') ?? 20)->get();
return view('business::expired-products.excel-csv', [
'expired_products' => $expired_products
]);
return view('business::expired-products.excel-csv', compact('expired_products'));
}
}

View File

@@ -3,22 +3,52 @@
namespace Modules\Business\App\Exports;
use App\Models\Product;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportExpiredProductReport implements FromView
{
use DateFilterTrait;
public function view(): View
{
return view('business::reports.expired-products.excel-csv', [
'expired_products' => Product::with('unit:id,unitName', 'brand:id,brandName', 'category:id,categoryName', 'stocks')
->withSum('stocks', 'productStock')
->where('business_id', auth()->user()->business_id)
->whereHas('stocks', function ($query) {
$query->whereDate('expire_date', '<', today())->where('productStock', '>', 0);
})
->latest()
->get()
]);
$businessId = auth()->user()->business_id;
$expiredProductsQuery = Product::with(['unit:id,unitName', 'brand:id,brandName', 'category:id,categoryName', 'stocks'])
->withSum('stocks', 'productStock')
->where('business_id', $businessId)
->whereHas('stocks', function ($query) {
$query->whereDate('expire_date', '<=', today())->where('productStock', '>', 0);
});
// Date Filter
$duration = request('custom_days') ?: 'today';
$expiredProductsQuery->whereHas('stocks', function ($q) use ($duration) {
$this->applyDateFilter($q, $duration, 'expire_date', request('from_date'), request('to_date'));
});
// Search Filter
if (request('search')) {
$search = request('search');
$expiredProductsQuery->where(function ($query) use ($search) {
$query->where('productName', 'like', '%' . $search . '%')
->orWhere('productCode', 'like', '%' . $search . '%')
->orWhere('productPurchasePrice', 'like', '%' . $search . '%')
->orWhere('productSalePrice', 'like', '%' . $search . '%')
->orWhereHas('category', function ($q) use ($search) {
$q->where('categoryName', 'like', '%' . $search . '%');
})
->orWhereHas('brand', function ($q) use ($search) {
$q->where('brandName', 'like', '%' . $search . '%');
})
->orWhereHas('unit', function ($q) use ($search) {
$q->where('unitName', 'like', '%' . $search . '%');
});
});
}
$expired_products = $expiredProductsQuery->latest()->limit(request('per_page') ?? 20)->get();
return view('business::reports.expired-products.excel-csv', compact('expired_products'));
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Modules\Business\App\Exports;
use App\Models\Sale;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportGuestDue implements FromView
{
public function view(): View
{
$walk_in_customers = Sale::where('business_id', auth()->user()->business_id)
->with('dueCollect')
->whereNull('party_id')
->where('dueAmount', '>', 0)
->when(request('search'), function ($query) {
$query->where(function ($q) {
$q->where('invoiceNumber', 'like', '%' . request('search') . '%')
->orwhere('dueAmount', 'like', '%' . request('search') . '%');
});
})
->latest()
->limit(request('per_page') ?? 20)->get();
return view('business::walk-dues.excel-csv', compact('walk_in_customers'));
}
}

View File

@@ -3,15 +3,44 @@
namespace Modules\Business\App\Exports;
use App\Models\Income;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportIncome implements FromView
{
use DateFilterTrait;
public function view(): View
{
return view('business::reports.income.excel-csv', [
'income_reports' => Income::with('category:id,categoryName', 'payment_type:id,name', 'branch:id,name', 'transactions')->where('business_id', auth()->user()->business_id)->latest()->get()
]);
$incomeQuery = Income::with('category:id,categoryName', 'payment_type:id,name', 'branch:id,name', 'transactions')
->where('business_id', auth()->user()->business_id);
// Branch filter
if (request('branch_id')) {
$incomeQuery->where('branch_id', request('branch_id'));
}
// Date Filter
$duration = request('custom_days') ?: 'today';
$this->applyDateFilter($incomeQuery, $duration, 'incomeDate', request('from_date'), request('to_date'));
// Search filter
if (request('search')) {
$search = request('search');
$incomeQuery->where(function ($query) use ($search) {
$query->where('incomeFor', 'like', '%' . $search . '%')
->orWhere('paymentType', 'like', '%' . $search . '%')
->orWhere('amount', 'like', '%' . $search . '%')
->orWhere('referenceNo', 'like', '%' . $search . '%')
->orWhereHas('category', fn($q) => $q->where('categoryName', 'like', '%' . $search . '%'))
->orWhereHas('payment_type', fn($q) => $q->where('name', 'like', '%' . $search . '%'))
->orWhereHas('branch', fn($q) => $q->where('name', 'like', '%' . $search . '%'));
});
}
$income_reports = $incomeQuery->latest()->limit(request('per_page') ?? 20)->get();
return view('business::reports.income.excel-csv', compact('income_reports'));
}
}

View File

@@ -2,117 +2,166 @@
namespace Modules\Business\App\Exports;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\FromView;
class ExportLossProfitHistory implements FromView
{
use DateFilterTrait;
public function view(): View
{
$user = auth()->user();
$businessId = $user->business_id;
$perPage = request('per_page') ?? 20;
$branchId = null;
if (moduleCheck('MultiBranchAddon')) {
$branchId = $user->branch_id ?? $user->active_branch_id;
}
// SALES
$dailySales = DB::table('sales')
$duration = request('custom_days') ?: 'today';
$salesQuery = DB::table('sales')
->select(
DB::raw('DATE(saleDate) as date'),
DB::raw('"saleDate"::date as date'),
DB::raw("
CASE
WHEN \"lossProfit\" >= 0 THEN 'positive'
ELSE 'negative'
END as profit_type
"),
DB::raw('SUM(actual_total_amount) as total_sales'),
DB::raw('SUM(lossProfit) as total_sale_income')
DB::raw('SUM("lossProfit") as total_sale_income')
)
->where('business_id', $businessId)
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
->groupBy(DB::raw('DATE(saleDate)'))
->get();
->when($branchId, fn ($q) =>
$q->where('branch_id', $branchId)
)
->groupBy(
DB::raw('"saleDate"::date'),
DB::raw("
CASE
WHEN \"lossProfit\" >= 0 THEN 'positive'
ELSE 'negative'
END
")
);
$sale_datas = $dailySales->map(fn($sale) => (object)[
'type' => 'Sale',
'date' => $sale->date,
'total_sales' => $sale->total_sales,
'total_incomes' => $sale->total_sale_income,
$this->applyDateFilter($salesQuery, $duration, 'saleDate', request('from_date'), request('to_date'));
$dailySales = $salesQuery->limit($perPage)->get();
$sale_datas = $dailySales->map(fn ($sale) => (object)[
'type' => 'Sale',
'date' => $sale->date,
'profit_type' => $sale->profit_type,
'total_sales' => $sale->total_sales,
'total_incomes' => $sale->total_sale_income,
]);
// INCOME
$dailyIncomes = DB::table('incomes')
$incomeQuery = DB::table('incomes')
->select(
DB::raw('DATE(incomeDate) as date'),
DB::raw('"incomeDate"::date as date'),
DB::raw('SUM(amount) as total_incomes')
)
->where('business_id', $businessId)
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
->groupBy(DB::raw('DATE(incomeDate)'))
->get();
->when($branchId, fn ($q) =>
$q->where('branch_id', $branchId)
)
->groupBy(DB::raw('"incomeDate"::date'));
$income_datas = $dailyIncomes->map(fn($income) => (object)[
'type' => 'Income',
'date' => $income->date,
$this->applyDateFilter($incomeQuery, $duration, 'incomeDate', request('from_date'), request('to_date'));
$dailyIncomes = $incomeQuery->limit($perPage)->get();
$income_datas = $dailyIncomes->map(fn ($income) => (object)[
'type' => 'Income',
'date' => $income->date,
'total_incomes' => $income->total_incomes,
]);
// MERGE SALE + INCOME
$mergedIncomeSaleData = collect();
$allDates = $dailySales->pluck('date')->merge($dailyIncomes->pluck('date'))->unique()->sort();
$allDates = $dailySales->pluck('date')
->merge($dailyIncomes->pluck('date'))
->unique()
->sort();
foreach ($allDates as $date) {
if ($income = $income_datas->firstWhere('date', $date)) {
$mergedIncomeSaleData->push($income);
}
if ($sale = $sale_datas->firstWhere('date', $date)) {
// multiple sale rows handle
$salesOfDate = $sale_datas->where('date', $date);
foreach ($salesOfDate as $sale) {
$mergedIncomeSaleData->push($sale);
}
}
// PAYROLL
$dailyPayrolls = collect();
if (moduleCheck('HrmAddon')) {
$dailyPayrolls = DB::table('payrolls')
$payrollQuery = DB::table('payrolls')
->select(
DB::raw('DATE(date) as date'),
DB::raw('"date"::date as date'),
DB::raw('SUM(amount) as total_payrolls')
)
->where('business_id', $businessId)
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
->groupBy(DB::raw('DATE(date)'))
->get();
->when($branchId, fn ($q) =>
$q->where('branch_id', $branchId)
)
->groupBy(DB::raw('"date"::date'));
$this->applyDateFilter($payrollQuery, $duration, 'date', request('from_date'), request('to_date'));
$dailyPayrolls = $payrollQuery->limit($perPage)->get();
}
// EXPENSES
$dailyExpenses = DB::table('expenses')
$expenseQuery = DB::table('expenses')
->select(
DB::raw('DATE(expenseDate) as date'),
DB::raw('"expenseDate"::date as date'),
DB::raw('SUM(amount) as total_expenses_only')
)
->where('business_id', $businessId)
->when($branchId, fn($q) => $q->where('branch_id', $branchId))
->groupBy(DB::raw('DATE(expenseDate)'))
->get();
->when($branchId, fn ($q) =>
$q->where('branch_id', $branchId)
)
->groupBy(DB::raw('"expenseDate"::date'));
$this->applyDateFilter($expenseQuery, $duration, 'expenseDate', request('from_date'), request('to_date'));
$dailyExpenses = $expenseQuery->limit($perPage)->get();
$mergedExpenseData = collect();
$allExpenseDates = $dailyExpenses->pluck('date')->merge($dailyPayrolls->pluck('date'))->unique()->sort();
$allExpenseDates = $dailyExpenses->pluck('date')
->merge($dailyPayrolls->pluck('date'))
->unique()
->sort();
foreach ($allExpenseDates as $date) {
if ($expense = $dailyExpenses->firstWhere('date', $date)) {
$mergedExpenseData->push((object)[
'type' => 'Expense',
'date' => $date,
'type' => 'Expense',
'date' => $date,
'total_expenses' => $expense->total_expenses_only,
]);
}
if ($payroll = $dailyPayrolls->firstWhere('date', $date)) {
$mergedExpenseData->push((object)[
'type' => 'Payroll',
'date' => $date,
'type' => 'Payroll',
'date' => $date,
'total_expenses' => $payroll->total_payrolls,
]);
}
}
// SUMMARY
$grossSaleProfit = $sale_datas->sum('total_sales');
$grossIncomeProfit = $income_datas->sum('total_incomes') + $sale_datas->sum('total_incomes');
$totalExpenses = $mergedExpenseData->sum('total_expenses');
$netProfit = $grossIncomeProfit - $totalExpenses;

View File

@@ -13,7 +13,13 @@ public function view(): View
$parties = Party::with('sales')
->where('business_id', auth()->user()->business_id)
->where('type', '!=', 'Supplier')
->when(request('search'), function ($query) {
$query->where(function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
})
->latest()
->limit(request('per_page') ?? 20)
->get();
return view('business::party-reports.loss-profit.excel-csv', compact('parties'));

View File

@@ -8,10 +8,61 @@
class ExportProduct implements FromView
{
public function view(): View
{
$products = Product::with('unit:id,unitName', 'brand:id,brandName', 'category:id,categoryName')->where('business_id', auth()->user()->business_id)->withSum('stocks as total_stock', 'productStock')->latest()->get();
$user = auth()->user();
$search = request('search');
$products = Product::query()
->where('business_id', $user->business_id)
->with([
'stocks',
'unit:id,unitName',
'brand:id,brandName',
'category:id,categoryName',
'warehouse:id,name',
'rack:id,name',
'shelf:id,name',
'combo_products.stock.product:id,productName'
])
->withSum('stocks as total_stock', 'productStock')
->where(function ($query) {
$query->where('product_type', '!=', 'combo')
->orWhere(function ($q) {
$q->where('product_type', 'combo')
->whereHas('combo_products');
});
})
->when($search, function ($q) use ($search) {
$q->where(function ($q) use ($search) {
$q->where('productName', 'like', "%{$search}%")
->orWhere('productCode', 'like', "%{$search}%")
->orWhere('hsn_code', 'like', "%{$search}%")
->orWhere('productPurchasePrice', 'like', "%{$search}%")
->orWhere('productSalePrice', 'like', "%{$search}%")
->orWhere('product_type', 'like', "%{$search}%")
->orWhereHas('category', fn ($q) => $q->where('categoryName', 'like', "%{$search}%"))
->orWhereHas('brand', fn ($q) => $q->where('brandName', 'like', "%{$search}%"))
->orWhereHas('unit', fn ($q) => $q->where('unitName', 'like', "%{$search}%"));
});
})
->latest()
->limit(request('per_page') ?? 20)
->get();
$products = $products->transform(function ($product) {
if ($product->product_type === 'combo') {
$product->total_stock = $product->combo_products->sum(
fn ($combo) => $combo->stock?->productStock ?? 0
);
$product->total_cost = $product->combo_products->sum(
fn ($combo) => ($combo->quantity ?? 0) * ($combo->purchase_price ?? 0)
);
}
return $product;
});
return view('business::products.excel-csv', [
'products' => $products

View File

@@ -11,23 +11,35 @@ class ExportProductLossProfit implements FromView
{
public function view(): View
{
$branchId = moduleCheck('MultiBranchAddon') ? auth()->user()->branch_id ?? auth()->user()->active_branch_id : null;
$user = auth()->user();
$branchId = $user->branch_id ?? $user->active_branch_id;
$product_lossProfits = SaleDetails::with('product:id,productName,productCode')
->whereHas('product', function ($q) {
$q->where('business_id', auth()->user()->business_id);
$baseQuery = SaleDetails::query()
->whereHas(
'product',
fn($q) =>
$q->where('business_id', $user->business_id)
)
->when(moduleCheck('MultiBranchAddon') && $branchId, function ($q) use ($branchId) {
$q->whereHas('sale', fn($q) => $q->where('branch_id', $branchId));
})
->when($branchId, function ($q) use ($branchId) {
$q->whereHas('sale', function ($sale) use ($branchId) {
$sale->where('branch_id', $branchId);
->when(request('search'), function ($q) {
$q->whereHas('product', function ($q) {
$q->where('productName', 'like', '%' . request('search') . '%')
->orWhere('productCode', 'like', '%' . request('search') . '%')
->orWhere('hsn_code', 'like', '%' . request('search') . '%');
});
})
});
$product_lossProfits = $baseQuery
->with('product:id,productName,productCode,hsn_code')
->select(
'product_id',
DB::raw('SUM(CASE WHEN lossProfit > 0 THEN lossProfit ELSE 0 END) as profit'),
DB::raw('SUM(CASE WHEN lossProfit < 0 THEN lossProfit ELSE 0 END) as loss')
DB::raw('SUM(CASE WHEN "lossProfit" > 0 THEN "lossProfit" ELSE 0 END) AS profit'),
DB::raw('SUM(CASE WHEN "lossProfit" < 0 THEN "lossProfit" ELSE 0 END) AS loss')
)
->groupBy('product_id')
->limit(request('per_page') ?? 20)
->get();
return view('business::reports.product-loss-profit.excel-csv', compact('product_lossProfits'));

View File

@@ -3,29 +3,54 @@
namespace Modules\Business\App\Exports;
use App\Models\Product;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportProductPurchaseHistoryDetailReport implements FromView
{
protected $id;
use DateFilterTrait;
public function __construct($id)
protected string $id;
public function __construct(string $id)
{
$this->id = $id;
}
public function view(): View
{
$businessId = auth()->user()->business_id;
$duration = request('custom_days') ?: 'today';
$product = Product::select('id', 'business_id', 'productName')
->where('business_id', auth()->user()->business_id)
->where('business_id', $businessId)
->findOrFail($this->id);
$purchaseDetailsQuery = $product->purchaseDetails()
->with('purchase:id,invoiceNumber,purchaseDate')
->whereHas('purchase', function ($purchase) use ($duration) {
$this->applyDateFilter($purchase, $duration, 'purchaseDate', request('from_date'), request('to_date'));
})
->with('purchase:id,party_id,invoiceNumber,purchaseDate', 'purchase.party:id,name')
->select('id', 'purchase_id', 'product_id', 'quantities', 'productPurchasePrice');
$purchaseDetails = $purchaseDetailsQuery->get();
$purchaseDetailsQuery->when(request('search'), function ($q) {
$search = request('search');
$q->where(function ($q) use ($search) {
$q->where('productPurchasePrice', 'like', "%{$search}%")
->orWhere('quantities', 'like', "%{$search}%")
->orWhereHas('purchase', function ($q) use ($search) {
$q->where('invoiceNumber', 'like', "%{$search}%")
->orWhere('purchaseDate', 'like', "%{$search}%");
})
->orWhereHas('purchase.party', function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%");
});
});
}
);
$purchaseDetails = $purchaseDetailsQuery->limit(request('per_page') ?? 20)->get();
return view('business::product-purchase-history-report.excel-csv-detail', compact('product', 'purchaseDetails'));
}

View File

@@ -3,17 +3,30 @@
namespace Modules\Business\App\Exports;
use App\Models\Product;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportProductPurchaseHistoryReport implements FromView
{
use DateFilterTrait;
public function view(): View
{
$businessId = auth()->user()->business_id;
$duration = request('custom_days') ?: 'today';
$productQuery = Product::with('saleDetails', 'purchaseDetails', 'stocks', 'combo_products')->where('business_id', $businessId);
$products = $productQuery->get();
$productQuery = Product::with(['saleDetails', 'purchaseDetails', 'purchaseDetails.purchase', 'stocks', 'combo_products'])
->where('business_id', $businessId)
->whereHas('purchaseDetails.purchase', function ($purchase) use ($duration) {
$this->applyDateFilter($purchase, $duration, 'purchaseDate', request('from_date'), request('to_date'));
});
$productQuery->when(request('search'), function ($q) {
$q->where('productName', 'like', '%' . $request->search . '%');
});
$products = $productQuery->limit(request('per_page') ?? 20)->get();
$total_purchase_qty = $products->sum(function ($product) {
return $product->purchaseDetails->sum('quantities');

View File

@@ -2,21 +2,44 @@
namespace Modules\Business\App\Exports;
use App\Models\Product;
use App\Models\PurchaseDetails;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportProductPurchaseReport implements FromView
{
use DateFilterTrait;
public function view(): View
{
$product_purchases = PurchaseDetails::with('product:id,productName', 'purchase:id,party_id,invoiceNumber,purchaseDate', 'purchase.party:id,name')
$query = PurchaseDetails::with('product:id,productName', 'purchase:id,party_id,invoiceNumber,purchaseDate', 'purchase.party:id,name')
->whereHas('purchase', function ($q) {
$q->where('business_id', auth()->user()->business_id);
})
->get();
});
// Date Filter
$duration = request('custom_days') ?: 'today';
$query->whereHas('purchase', function ($q) use ($duration) {
$this->applyDateFilter($q, $duration, 'purchaseDate', request('from_date'), request('to_date'));
});
$query->when(request('search'), function ($q) {
$q->where(function ($q) {
$q->whereHas('product', function ($q) {
$q->where('productName', 'like', '%' . request('search') . '%');
})
->orWhereHas('purchase', function ($q) {
$q->where('invoiceNumber', 'like', '%' . request('search') . '%')
->orWhere('purchaseDate', 'like', '%' . request('search') . '%');
})
->orWhereHas('purchase.party', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
});
});
$product_purchases = $query->limit(request('per_page') ?? 20)->get();
return view('business::reports.product-purchase.excel-csv', compact('product_purchases'));
}

View File

@@ -3,29 +3,53 @@
namespace Modules\Business\App\Exports;
use App\Models\Product;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportProductSaleHistoryDetailReport implements FromView
{
protected $id;
use DateFilterTrait;
public function __construct($id)
protected string $id;
public function __construct(string $id)
{
$this->id = $id;
}
public function view(): View
{
$businessId = auth()->user()->business_id;
$duration = request('custom_days') ?: 'today';
$product = Product::select('id', 'business_id', 'productName')
->where('business_id', auth()->user()->business_id)
->where('business_id', $businessId)
->findOrFail($this->id);
$saleDetailsQuery = $product->saleDetails()
->whereHas('sale', function ($sale) use ($duration) {
$this->applyDateFilter($sale, $duration, 'saleDate', request('from_date'), request('to_date'));
})
->with('sale:id,party_id,invoiceNumber,saleDate', 'sale.party:id,name')
->select('id', 'sale_id', 'product_id', 'quantities', 'lossprofit', 'price', 'productPurchasePrice');
$saleDetails = $saleDetailsQuery->get();
$saleDetailsQuery->when(request('search'), function ($q) {
$search = request('search');
$q->where(function ($q) use ($search) {
$q->where('price', 'like', "%{$search}%")
->orWhere('quantities', 'like', "%{$search}%")
->orWhereHas('sale', function ($q) use ($search) {
$q->where('invoiceNumber', 'like', "%{$search}%")
->orWhere('saleDate', 'like', "%{$search}%");
})
->orWhereHas('sale.party', function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%");
});
});
}
);
$saleDetails = $saleDetailsQuery->limit(request('per_page') ?? 20)->get();
return view('business::product-sale-history-report.excel-csv-detail', compact('product', 'saleDetails'));
}

View File

@@ -3,17 +3,30 @@
namespace Modules\Business\App\Exports;
use App\Models\Product;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportProductSaleHistoryReport implements FromView
{
use DateFilterTrait;
public function view(): View
{
$businessId = auth()->user()->business_id;
$duration = request('custom_days') ?: 'today';
$productQuery = Product::with('saleDetails', 'purchaseDetails', 'stocks', 'combo_products')->where('business_id', $businessId);
$products = $productQuery->get();
$productQuery = Product::with(['saleDetails', 'purchaseDetails', 'saleDetails.sale', 'stocks', 'combo_products'])
->where('business_id', $businessId)
->whereHas('saleDetails.sale', function ($sale) use ($duration) {
$this->applyDateFilter($sale, $duration, 'saleDate', request('from_date'), request('to_date'));
});
$productQuery->when(request('search'), function ($q) {
$q->where('productName', 'like', '%' . request('search') . '%');
});
$products = $productQuery->limit(request('per_page') ?? 20)->get();
$total_single_sale_price = $products->sum(function ($product) {
return $product->saleDetails->sum('price');

View File

@@ -3,18 +3,45 @@
namespace Modules\Business\App\Exports;
use App\Models\Sale;
use App\Models\SaleDetails;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportProductSaleReport implements FromView
{
use DateFilterTrait;
public function view(): View
{
$product_sales = Sale::with('details:id,sale_id,product_id,quantities,price', 'details.product:id,productName', 'party:id,name')
->where('business_id', auth()->user()->business_id)
->latest()
->get();
$query = SaleDetails::with('product:id,productName', 'sale:id,party_id,invoiceNumber,saleDate', 'sale.party:id,name')
->whereHas('sale', function ($q) {
$q->where('business_id', auth()->user()->business_id);
});
// Date Filter
$duration = request('custom_days') ?: 'today';
$query->whereHas('sale', function ($q) use ($duration) {
$this->applyDateFilter($q, $duration, 'saleDate', request('from_date'), request('to_date'));
});
// Search Filter
$query->when(request('search'), function ($q) {
$q->where(function ($q) {
$q->whereHas('product', function ($q) {
$q->where('productName', 'like', '%' . request('search') . '%');
})
->orWhereHas('sale', function ($q) {
$q->where('invoiceNumber', 'like', '%' . request('search') . '%')
->orWhere('saleDate', 'like', '%' . request('search') . '%');
})
->orWhereHas('sale.party', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
});
});
$product_sales = $query->limit(request('per_page') ?? 20)->get();
return view('business::reports.product-sale.excel-csv', compact('product_sales'));
}

View File

@@ -3,14 +3,17 @@
namespace Modules\Business\App\Exports;
use App\Models\Purchase;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportPurchaseReturn implements FromView
{
use DateFilterTrait;
public function view(): View
{
$purchases = Purchase::with([
$purchasesQuery = Purchase::with([
'user:id,name',
'branch:id,name',
'party:id,name,email,phone,type',
@@ -22,9 +25,32 @@ public function view(): View
}
])
->where('business_id', auth()->user()->business_id)
->whereHas('purchaseReturns')
->latest()
->get();
->whereHas('purchaseReturns');
$purchasesQuery->when(request('branch_id'), function ($q) {
$q->where('branch_id', request('branch_id'));
});
// Date Filter
$duration = request('custom_days') ?: 'today';
$purchasesQuery->whereHas('purchaseReturns', function ($query) use ($duration) {
$this->applyDateFilter($query, $duration, 'return_date', request('from_date'), request('to_date'));
});
// Search Filter
if (request('search')) {
$purchasesQuery->where(function ($query) {
$query->where('invoiceNumber', 'like', '%' . request('search') . '%')
->orWhereHas('party', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
})
->orWhereHas('branch', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
});
}
$purchases = $purchasesQuery->latest()->limit(request('per_page') ?? 20)->get();
return view('business::reports.purchase-return.excel-csv', compact('purchases'));
}

View File

@@ -3,16 +3,46 @@
namespace Modules\Business\App\Exports;
use App\Models\Sale;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportSaleReport implements FromView
{
use DateFilterTrait;
public function view(): View
{
$sales = Sale::with('user:id,name', 'party:id,name,email,phone,type', 'payment_type:id,name', 'branch:id,name', 'transactions')->where('business_id', auth()->user()->business_id)
->latest()
->get();
$salesQuery = Sale::with('user:id,name', 'party:id,name,email,phone,type', 'payment_type:id,name', 'branch:id,name', 'transactions')
->where('business_id', auth()->user()->business_id);
$salesQuery->when(request('branch_id'), function ($q) {
$q->where('branch_id', request('branch_id'));
});
// Date Filter
$duration = request('custom_days') ?: 'today';
$this->applyDateFilter($salesQuery, $duration, 'saleDate', request('from_date'), request('to_date'));
// Search Filter
if (request('search')) {
$salesQuery->where(function ($query) {
$query->where('paymentType', 'like', '%' . request('search') . '%')
->orWhere('invoiceNumber', 'like', '%' . request('search') . '%')
->orWhereHas('party', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
})
->orWhereHas('payment_type', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
})
->orWhereHas('branch', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
});
}
$sales = $salesQuery->latest()->limit(request('per_page') ?? 20)->get();
return view('business::reports.sales.excel', compact('sales'));
}

View File

@@ -3,14 +3,17 @@
namespace Modules\Business\App\Exports;
use App\Models\Sale;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportSalesReturn implements FromView
{
use DateFilterTrait;
public function view(): View
{
$sales = Sale::with([
$salesQuery = Sale::with([
'user:id,name',
'party:id,name',
'branch:id,name',
@@ -23,9 +26,30 @@ public function view(): View
}
])
->where('business_id', auth()->user()->business_id)
->whereHas('saleReturns')
->latest()
->get();
->when(request('branch_id'), function ($q) {
$q->where('branch_id', request('branch_id'));
})->whereHas('saleReturns');
// Date Filter
$duration = request('custom_days') ?: 'today';
$salesQuery->whereHas('saleReturns', function ($query) use ($duration) {
$this->applyDateFilter($query, $duration, 'return_date', request('from_date'), request('to_date'));
});
// Search Filter
if (request('search')) {
$salesQuery->where(function ($query) {
$query->where('invoiceNumber', 'like', '%' . request('search') . '%')
->orWhereHas('party', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
})
->orWhereHas('branch', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
});
}
$sales = $salesQuery->latest()->limit(request('per_page') ?? 20)->get();
return view('business::reports.sales-return.excel-csv', compact('sales'));
}

View File

@@ -2,15 +2,17 @@
namespace Modules\Business\App\Exports;
use App\Models\Party;
use Maatwebsite\Excel\Concerns\FromView;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Collection;
class ExportSingleCustomerLedger implements FromView
{
public $ledger;
public $party;
public Collection $ledger;
public Party $party;
public function __construct($ledger, $party)
public function __construct(Collection $ledger, Party $party)
{
$this->ledger = $ledger;
$this->party = $party;

View File

@@ -2,15 +2,17 @@
namespace Modules\Business\App\Exports;
use App\Models\Party;
use Maatwebsite\Excel\Concerns\FromView;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Collection;
class ExportSingleSupplierLedger implements FromView
{
public $ledger;
public $party;
public Collection $ledger;
public Party $party;
public function __construct($ledger, $party)
public function __construct(Collection $ledger, Party $party)
{
$this->ledger = $ledger;
$this->party = $party;

View File

@@ -3,15 +3,44 @@
namespace Modules\Business\App\Exports;
use App\Models\PlanSubscribe;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportSubscription implements FromView
{
use DateFilterTrait;
public function view(): View
{
return view('business::reports.subscription-reports.excel-csv', [
'subscribers' => PlanSubscribe::with(['plan:id,subscriptionName','business:id,companyName,business_category_id,pictureUrl','business.category:id,name','gateway:id,name'])->where('business_id', auth()->user()->business_id)->latest()->get()
]);
$subscriberQuery = PlanSubscribe::with(['plan:id,subscriptionName','business:id,companyName,business_category_id,pictureUrl','business.category:id,name','gateway:id,name'])->where('business_id', auth()->user()->business_id);
// Date Filter
$duration = request('custom_days') ?: 'today';
$this->applyDateFilter($subscriberQuery, $duration, 'created_at', request('from_date'), request('to_date'));
// Search Filter
if (request('search')) {
$search = request('search');
$subscriberQuery->where(function ($query) use ($search) {
$query->where('duration', 'like', '%' . $search . '%')
->orWhereHas('plan', function ($q) use ($search) {
$q->where('subscriptionName', 'like', '%' . $search . '%');
})
->orWhereHas('gateway', function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%');
})
->orWhereHas('business', function ($q) use ($search) {
$q->where('companyName', 'like', '%' . $search . '%')
->orWhereHas('category', function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%');
});
});
});
}
$subscribers = $subscriberQuery->latest()->limit(request('per_page') ?? 20)->get();
return view('business::reports.subscription-reports.excel-csv', compact('subscribers'));
}
}

View File

@@ -16,31 +16,48 @@ public function view(): View
$query = Party::where('business_id', $businessId)
->where('type', 'Supplier')
->when(request('search'), function ($q) {
$q->where(function ($q2) {
$q2->where('type', 'like', '%' . request('search') . '%')
->orWhere('name', 'like', '%' . request('search') . '%')
->orWhere('phone', 'like', '%' . request('search') . '%')
->orWhere('credit_limit', 'like', '%' . request('search') . '%');
});
})
->with('purchases_dues')
->latest();
if ($activeBranch) {
$query->whereHas('purchases_dues', function ($q) use ($activeBranch) {
$q->where('branch_id', $activeBranch->id)
->where('dueAmount', '>', 0);
});
} else {
$query->where('due', '>', 0);
}
$parties = $query->get();
if ($activeBranch) {
$parties->transform(function ($supplier) use ($activeBranch) {
$supplier->due = $supplier->purchases_dues
->where('branch_id', $activeBranch->id)
->sum('dueAmount');
return $supplier;
})->filter(fn($supplier) => $supplier->due > 0);
$parties = $parties
->transform(function ($supplier) use ($activeBranch) {
$party_due = $supplier->purchases_dues
->where('branch_id', $activeBranch->id)
->sum('dueAmount');
if ($supplier->branch_id === $activeBranch->id) {
$openingBalanceDue = $supplier->opening_balance_type === 'due'
? ($supplier->opening_balance ?? 0)
: 0;
$supplier->due = $openingBalanceDue + $party_due;
} else {
$supplier->due = $party_due;
}
return $supplier;
})
->filter(fn($supplier) => $supplier->due > 0)
->take(request('per_page') ?? 20)
->values();
} else {
$parties = $parties
->filter(fn($supplier) => ($supplier->due ?? 0) > 0)
->take(request('per_page') ?? 20)
->values();
}
return view('business::reports.supplier-due.excel-csv', [
'parties' => $parties
]);
return view('business::reports.supplier-due.excel-csv', compact('parties'));
}
}

View File

@@ -13,20 +13,27 @@ public function view(): View
$suppliers = Party::with('purchases')
->where('business_id', auth()->user()->business_id)
->where('type', '=', 'Supplier')
->when(request('search'), function ($q) {
$q->where(function ($q) {
$q->where('name', 'like', '%' . request('search') . '%')
->orWhere('phone', 'like', '%' . request('search') . '%')
->orWhere('type', 'like', '%' . request('search') . '%');
});
})
->latest()
->get();
->limit(request('per_page') ?? 20)->get();
$totalAmount = $suppliers->sum(function ($customer) {
$totalAmount = $suppliers->sum(function ($customer) {
return $customer->purchases?->sum('totalAmount') ?? 0;
});
});
$totalPaid = $suppliers->sum(function ($customer) {
return $customer->purchases?->sum('paidAmount') ?? 0;
});
$totalPaid = $suppliers->sum(function ($customer) {
return $customer->purchases?->sum('paidAmount') ?? 0;
});
$totalDue = $suppliers->sum(function ($customer) {
return $customer->purchases?->sum('dueAmount') ?? 0;
});
$totalDue = $suppliers->sum(function ($customer) {
return $customer->purchases?->sum('dueAmount') ?? 0;
});
return view('business::party-reports.supplier-ledger.excel-csv', compact('suppliers', 'totalAmount', 'totalPaid', 'totalDue'));
}

View File

@@ -0,0 +1,211 @@
<?php
namespace Modules\Business\App\Exports;
use App\Models\Purchase;
use App\Models\PurchaseReturnDetail;
use App\Models\Sale;
use App\Models\SaleDetails;
use App\Models\SaleReturnDetails;
use App\Models\Vat;
use App\Models\VatTransaction;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportTaxReport implements FromView
{
use DateFilterTrait;
public function view(): View
{
$businessId = auth()->user()->business_id;
$type = request('type') ?? 'sales';
$duration = request('custom_days') ?: 'today';
// SALES QUERY
$salesQuery = Sale::with('party', 'payment_type', 'transactions')
->where('business_id', $businessId);
$this->applyDateFilter($salesQuery, $duration, 'saleDate', request('from_date'), request('to_date'));
if (request('search')) {
$search = request('search');
$salesQuery->where(function ($query) use ($search) {
$query->where('invoiceNumber', 'like', '%' . $search . '%')
->orWhere('totalAmount', 'like', '%' . $search . '%')
->orWhere('discountAmount', 'like', '%' . $search . '%')
->orWhereHas('party', fn($q) => $q->where('name', 'like', '%' . $search . '%')->orWhere('tax_no', 'like', '%' . $search . '%'));
});
}
$salesVatTotals = VatTransaction::where('vat_transactions.business_id', $businessId)
->where('vat_transactions.vatable_type', SaleDetails::class)
->join('sale_details', 'sale_details.id', '=', 'vat_transactions.vatable_id')
->join('sales', 'sales.id', '=', 'sale_details.sale_id')
->where('sales.business_id', $businessId)
->selectRaw('vat_transactions.vat_id, SUM(vat_transactions.vat_amount) as total')
->groupBy('vat_transactions.vat_id')
->pluck('total','vat_transactions.vat_id')
->toArray();
$salesReturnVatTotals = VatTransaction::where('vat_transactions.business_id', $businessId)
->where('vat_transactions.vatable_type', SaleReturnDetails::class)
->join('sale_return_details', 'sale_return_details.id', '=', 'vat_transactions.vatable_id')
->join('sale_details', 'sale_details.id', '=', 'sale_return_details.sale_detail_id')
->join('sales', 'sales.id', '=', 'sale_details.sale_id')
->where('sales.business_id', $businessId)
->selectRaw('vat_transactions.vat_id, SUM(vat_transactions.vat_amount) as total')
->groupBy('vat_transactions.vat_id')
->pluck('total', 'vat_transactions.vat_id')
->toArray();
$saleVatRowMap = VatTransaction::where('business_id', $businessId)
->where('vatable_type', \App\Models\SaleDetails::class)
->whereIn('vatable_id', function ($query) use ($businessId, $duration) {
$query->select('sale_details.id')
->from('sale_details')
->join('sales', 'sales.id', '=', 'sale_details.sale_id')
->where('sales.business_id', $businessId);
if ($duration === 'custom_days' && request('from_date') && request('to_date')) {
$query->whereBetween('sales.saleDate', [request('from_date'), request('to_date')]);
}
})
->join('sale_details', 'sale_details.id', '=', 'vat_transactions.vatable_id')
->selectRaw('sale_details.sale_id, vat_transactions.vat_id, SUM(vat_transactions.vat_amount) as total')
->groupBy('sale_details.sale_id', 'vat_transactions.vat_id')
->get()
->groupBy('sale_id')
->map(function ($items) {
return $items->pluck('total', 'vat_id')->toArray();
})
->toArray();
$saleReturnVatRows = VatTransaction::where('vat_transactions.business_id', $businessId)
->where('vat_transactions.vatable_type', SaleReturnDetails::class)
->join('sale_return_details','sale_return_details.id','=','vat_transactions.vatable_id')
->join('sale_details','sale_details.id','=','sale_return_details.sale_detail_id')
->join('sales','sales.id','=','sale_details.sale_id')
->selectRaw('sales.id as sale_id, vat_transactions.vat_id, SUM(vat_transactions.vat_amount) as vat_amount')
->groupBy('sales.id','vat_transactions.vat_id')
->get();
$saleReturnVatRowMap = $saleReturnVatRows
->groupBy('sale_id')
->map(fn($rows)=>$rows->pluck('vat_amount','vat_id'))
->toArray();
// PURCHASE QUERY
$purchasesQuery = Purchase::with('party', 'payment_type', 'transactions')
->where('business_id', $businessId);
$this->applyDateFilter($purchasesQuery, $duration, 'purchaseDate', request('from_date'), request('to_date'));
if (request('search')) {
$search = request('search');
$purchasesQuery->where(function ($query) use ($search) {
$query->where('invoiceNumber', 'like', '%' . $search . '%')
->orWhere('totalAmount', 'like', '%' . $search . '%')
->orWhere('discountAmount', 'like', '%' . $search . '%')
->orWhereHas('party', fn($q) => $q->where('name', 'like', '%' . $search . '%')->orWhere('tax_no', 'like', '%' . $search . '%'));
});
}
$purchasesVatTotals = VatTransaction::where('vat_transactions.business_id', $businessId)
->where('vat_transactions.vatable_type', \App\Models\PurchaseDetails::class)
->join('purchase_details', 'purchase_details.id', '=', 'vat_transactions.vatable_id')
->join('purchases', 'purchases.id', '=', 'purchase_details.purchase_id')
->where('purchases.business_id', $businessId)
->selectRaw('vat_transactions.vat_id, SUM(vat_transactions.vat_amount) as total')
->groupBy('vat_transactions.vat_id')
->pluck('total','vat_transactions.vat_id')
->toArray();
$purchaseReturnVatTotals = VatTransaction::where('vat_transactions.business_id', $businessId)
->where('vat_transactions.vatable_type', PurchaseReturnDetail::class)
->join('purchase_return_details', 'purchase_return_details.id', '=', 'vat_transactions.vatable_id')
->join('purchase_details', 'purchase_details.id', '=', 'purchase_return_details.purchase_detail_id')
->join('purchases', 'purchases.id', '=', 'purchase_details.purchase_id')
->where('purchases.business_id', $businessId)
->selectRaw('vat_transactions.vat_id, SUM(vat_transactions.vat_amount) as total')
->groupBy('vat_transactions.vat_id')
->pluck('total', 'vat_transactions.vat_id')
->toArray();
$purchaseVatRowMap = VatTransaction::where('business_id', $businessId)
->where('vatable_type', \App\Models\PurchaseDetails::class)
->whereIn('vatable_id', function ($query) use ($businessId, $duration) {
$query->select('purchase_details.id')
->from('purchase_details')
->join('purchases', 'purchases.id', '=', 'purchase_details.purchase_id')
->where('purchases.business_id', $businessId);
if ($duration === 'custom_date' && request('from_date') && request('to_date')) {
$query->whereBetween('purchases.purchaseDate', [request('from_date'), request('to_date')]);
}
})
->join('purchase_details', 'purchase_details.id', '=', 'vat_transactions.vatable_id')
->selectRaw('purchase_details.purchase_id, vat_transactions.vat_id, SUM(vat_transactions.vat_amount) as total')
->groupBy('purchase_details.purchase_id', 'vat_transactions.vat_id')
->get()
->groupBy('purchase_id')
->map(function ($items) {
return $items->pluck('total', 'vat_id')->toArray();
})
->toArray();
$purchaseReturnVatRows = VatTransaction::where('vat_transactions.business_id', $businessId)
->where('vat_transactions.vatable_type', PurchaseReturnDetail::class)
->join('purchase_return_details','purchase_return_details.id','=','vat_transactions.vatable_id')
->join('purchase_details','purchase_details.id','=','purchase_return_details.purchase_detail_id')
->join('purchases','purchases.id','=','purchase_details.purchase_id')
->selectRaw('purchases.id as purchase_id, vat_transactions.vat_id, SUM(vat_transactions.vat_amount) as vat_amount')
->groupBy('purchases.id','vat_transactions.vat_id')
->get();
$purchaseReturnVatRowMap = $purchaseReturnVatRows
->groupBy('purchase_id')
->map(fn($rows) => $rows->pluck('vat_amount','vat_id'))
->toArray();
// TAB CONTROL
$sales = collect();
$purchases = collect();
$limit = request('per_page') ?? 20;
if ($type === 'sales') {
$sales = $salesQuery->latest()->limit($limit)->get();
}
if ($type === 'purchases') {
$purchases = $purchasesQuery->latest()->limit($limit)->get();
}
$vats = Vat::where('business_id', $businessId)->get();
return view('business::reports.vats.excel-csv',
compact(
'sales',
'salesVatTotals',
'salesReturnVatTotals',
'saleVatRowMap',
'saleReturnVatRowMap',
'purchases',
'purchasesVatTotals',
'purchaseReturnVatTotals',
'purchaseVatRowMap',
'purchaseReturnVatRowMap',
'vats',
'type'
)
);
}
}

View File

@@ -14,10 +14,23 @@ public function view(): View
->where('business_id', auth()->user()->business_id)
->where('type', '!=', 'Supplier')
->whereHas('sales')
->when(request('search'), function ($q) {
$q->where(function ($q) {
$q->where('name', 'like', '%' . request('search') . '%')
->orWhere('phone', 'like', '%' . request('search') . '%')
->orWhere('email', 'like', '%' . request('search') . '%')
->orWhere('type', 'like', '%' . request('search') . '%');
});
})
->withCount('sales')
->withSum('sales', 'totalAmount')
->orderByDesc('sales_count')
->orderByDesc('sales_sum_total_amount')
->when(request('type'), function ($q) {
$q->where(function ($q) {
$q->where('type', request('type'));
});
})
->take(5)
->get();

View File

@@ -11,26 +11,39 @@ class ExportTopProduct implements FromView
{
public function view(): View
{
$branchId = moduleCheck('MultiBranchAddon') ? auth()->user()->branch_id ?? auth()->user()->active_branch_id : null;
$user = auth()->user();
$businessId = $user->business_id;
$top_products = SaleDetails::with('product:id,productName,productCode')
->whereHas('product', function ($q) {
$q->where('business_id', auth()->user()->business_id);
})
->when($branchId, function ($q) use ($branchId) {
$q->whereHas('sale', function ($sale) use ($branchId) {
$sale->where('branch_id', $branchId);
});
})
->select(
'product_id',
DB::raw('SUM(quantities) as total_sold_qty'),
DB::raw('SUM(price * quantities) as total_sale_amount')
)
->groupBy('product_id')
->orderByDesc('total_sold_qty')
->take(5)
->get();
$branchId = null;
if (moduleCheck('MultiBranchAddon')) {
$branchId = $user->branch_id ?? $user->active_branch_id;
}
$top_products = SaleDetails::query()
->with('product:id,productName,productCode,hsn_code')
->whereHas('product', function ($q) use ($businessId) {
$q->where('business_id', $businessId);
})
->when(request('search'), function ($q) {
$q->whereHas('product', function ($q) {
$q->where('productName', 'like', '%' . request('search') . '%')
->orWhere('productCode', 'like', '%' . request('search') . '%');
});
})
->when($branchId, function ($q) use ($branchId) {
$q->whereHas('sale', function ($q) use ($branchId) {
$q->where('branch_id', $branchId);
});
})
->select(
'product_id',
DB::raw('SUM(quantities) as total_sold_qty'),
DB::raw('SUM((price - discount) * quantities) as total_sale_amount')
)
->groupBy('product_id')
->orderByDesc('total_sold_qty')
->limit(5)
->get();
return view('business::reports.top-products.excel-csv', compact('top_products'));
}

View File

@@ -14,6 +14,14 @@ public function view(): View
->where('business_id', auth()->user()->business_id)
->where('type', '=', 'Supplier')
->whereHas('purchases')
->when(request('search'), function ($q) {
$q->where(function ($q) {
$q->where('name', 'like', '%' . request('search') . '%')
->orWhere('phone', 'like', '%' . request('search') . '%')
->orWhere('email', 'like', '%' . request('search') . '%')
->orWhere('type', 'like', '%' . request('search') . '%');
});
})
->withCount('purchases')
->withSum('purchases', 'totalAmount')
->orderByDesc('purchases_count')

View File

@@ -3,15 +3,49 @@
namespace Modules\Business\App\Exports;
use App\Models\DueCollect;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportTransaction implements FromView
{
use DateFilterTrait;
public function view(): View
{
return view('business::reports.transaction-history.excel-csv', [
'transactions' => DueCollect::where('business_id', auth()->user()->business_id)->with('party:id,name,type', 'payment_type:id,name', 'transactions')->latest()->get()
]);
$businessId = auth()->user()->business_id;
$transactionsQuery = DueCollect::with('party:id,name,type', 'payment_type:id,name', 'transactions')->where('business_id', $businessId);
// Date Filter
$duration = request('custom_days') ?: 'today';
$this->applyDateFilter($transactionsQuery, $duration, 'paymentDate', request('from_date'), request('to_date'));
// Search Filter
if (request('search')) {
$transactionsQuery->where(function ($query) {
$query->where('paymentType', 'like', '%' . request('search') . '%')
->orWhere('totalDue', 'like', '%' . request('search') . '%')
->orWhere('invoiceNumber', 'like', '%' . request('search') . '%')
->orWhere('payDueAmount', 'like', '%' . request('search') . '%')
->orWhereHas('party', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
})
->orWhereHas('payment_type', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
});
}
if (request('type')) {
$transactionsQuery->whereHas('party', function ($q) {
$q->where(function ($q) {
$q->where('type', request('type'));
});
});
}
$transactions = $transactionsQuery->latest()->limit(request('per_page'))->get();
return view('business::reports.transaction-history.excel-csv', compact('transactions'));
}
}

View File

@@ -3,17 +3,82 @@
namespace Modules\Business\App\Exports;
use App\Models\Transaction;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class ExportTransactionReport implements FromView
{
use DateFilterTrait;
public function view(): View
{
$transactions = Transaction::with('paymentType')
->where('business_id', auth()->user()->business_id)
->latest()
->get();
$businessId = auth()->user()->business_id;
$transactionsQuery = Transaction::with('paymentType')
->where('business_id', $businessId);
$duration = request('custom_days') ?: 'today';
$this->applyDateFilter($transactionsQuery, $duration, 'date', request('from_date'), request('to_date'));
// Search filter
if (request('search')) {
$transactionsQuery->where(function ($query) {
$query->where('amount', 'like', '%' . request('search') . '%')
->orWhere('invoice_no', 'like', '%' . request('search') . '%')
->orWhere('platform', 'like', '%' . request('search') . '%')
->orWhere('type', 'like', '%' . request('search') . '%');
});
}
// Platform filter
if (request('platform')) {
$transactionsQuery->where('platform', request('platform'));
}
// Party Filter
if (request('party_id')) {
$transactionsQuery->where(function ($q) {
$q->where(function ($saleQ) {
$saleQ->where('platform', 'sale')
->whereHas('sale', function ($s) {
$s->where('party_id', request('party_id'));
});
});
$q->where(function ($saleRtnQ) {
$saleRtnQ->where('platform', 'sale_return')
->whereHas('saleReturn.sale', function ($s) {
$s->where('party_id', request('party_id'));
});
});
$q->orWhere(function ($purQ) {
$purQ->where('platform', 'purchase')
->whereHas('purchase', function ($p) {
$p->where('party_id', request('party_id'));
});
});
$q->orWhere(function ($purRtnQ) {
$purRtnQ->where('platform', 'purchase_return')
->whereHas('purchaseReturn.purchase', function ($p) {
$p->where('party_id', request('party_id'));
});
});
$q->orWhere(function ($dueQ) {
$dueQ->where('platform', 'due_collect')
->whereHas('dueCollect', function ($d) {
$d->where('party_id', request('party_id'));
});
});
});
}
$transactions = $transactionsQuery->latest()->limit(request('per_page') ?? 20)->get();
return view('business::transactions.excel-csv', compact('transactions'));
}

View File

@@ -10,8 +10,45 @@ class ExportTransfer implements FromView
{
public function view(): View
{
return view('business::transfers.excel-csv', [
'transfers' => Transfer::with(['fromWarehouse:id,name', 'toWarehouse:id,name', 'toBranch:id,name', 'fromBranch:id,name', 'transferProducts'])->where('business_id', auth()->user()->business_id)->latest()->get()
]);
$user = auth()->user();
$activeBranchId = $user->active_branch_id;
$transfers = Transfer::with(['fromWarehouse:id,name', 'toWarehouse:id,name', 'toBranch:id,name', 'fromBranch:id,name', 'transferProducts'])
->where('business_id', $user->business_id)
->when($activeBranchId, function ($q) use ($activeBranchId) {
$q->where(function ($query) use ($activeBranchId) {
$query->where('from_branch_id', $activeBranchId)
->orWhere('to_branch_id', $activeBranchId);
});
})
->when(request('branch_id') && !$activeBranchId, function ($q) {
$q->where(function ($query) {
$query->where('from_branch_id', request('branch_id'))->orWhere('to_branch_id', request('branch_id'));
});
})
->when(request('search'), function ($q) {
$search = request('search');
$q->where(function ($q) use ($search) {
$q->where('note', 'like', '%' . $search . '%')
->orWhere('status', 'like', '%' . $search . '%')
->orWhere('invoice_no', 'like', '%' . $search . '%')
->orWhereHas('fromWarehouse', function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%');
})
->orWhereHas('toWarehouse', function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%');
})
->orWhereHas('toBranch', function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%');
})
->orWhereHas('fromBranch', function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%');
});
});
})
->latest()
->limit(request('per_page') ?? 20)->get();
return view('business::transfers.excel-csv', compact('transfers'));
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace Modules\Business\App\Exports;
use App\Models\Purchase;
use App\Traits\DateFilterTrait;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
class PurchaseExport implements FromView
{
use DateFilterTrait;
public function view(): View
{
$purchasesQuery = Purchase::with('user:id,name', 'party:id,name,email,phone,type', 'payment_type:id,name', 'branch:id,name', 'transactions')
->where('business_id', auth()->user()->business_id);
$purchasesQuery->when(request('branch_id'), function ($q) {
$q->where('branch_id', request('branch_id'));
});
// Date Filter
$duration = request('custom_days') ?: 'today';
$this->applyDateFilter($purchasesQuery, $duration, 'purchaseDate', request('from_date'), request('to_date'));
// Search Filter
if (request('search')) {
$purchasesQuery->where(function ($query) {
$query->where('paymentType', 'like', '%' . request('search') . '%')
->orWhere('invoiceNumber', 'like', '%' . request('search') . '%')
->orWhereHas('party', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
})
->orWhereHas('payment_type', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
})
->orWhereHas('branch', function ($q) {
$q->where('name', 'like', '%' . request('search') . '%');
});
});
}
$purchases = $purchasesQuery->latest()->limit($request->per_page ?? 20)->get();
return view('business::reports.purchase.excel-csv', compact('purchases'));
}
}

View File

@@ -100,28 +100,33 @@ public function exportCsv()
public function exportPdf(Request $request)
{
$businessId = auth()->user()->business_id;
$duration = $request->custom_days ? : 'today';
$fromDate = $request->from_date;
$toDate = $request->to_date;
$perPage = $request->per_page ?? 20;
[$filter_from_date, $filter_to_date] = $this->applyDateRange($duration, $request->from_date, $request->to_date);
// PRODUCT
$productQuery = Product::select('id', 'business_id', 'productName', 'product_type', 'created_at')
->with(['stocks:id,business_id,product_id,productStock,productPurchasePrice', 'combo_products.stock'])
->where('business_id', $businessId);
$this->applyDateFilter($productQuery, $duration, 'created_at', $fromDate, $toDate);
$products = $productQuery->get()
->map(function ($item) {
$item->source = 'product';
return $item;
});
// BANK
$bankQuery = PaymentType::where('business_id', $businessId);
$this->applyDateFilter($bankQuery, $duration, 'opening_date', $fromDate, $toDate);
$banks = $bankQuery->get()
->map(function ($item) {
$item->source = 'bank';
return $item;
});
$asset_datas = $products->merge($banks);
$asset_datas = $products->merge($banks)->take($perPage);
// TOTAL STOCK VALUE
$total_stock_value = 0;
foreach ($products as $product) {
if (in_array($product->product_type, ['single', 'variant'])) {
@@ -143,6 +148,6 @@ public function exportPdf(Request $request)
$totalBankBalance = $banks->sum('balance');
$total_asset = $total_stock_value + $totalBankBalance;
return PdfService::render('business::balance-sheets.pdf', compact('asset_datas', 'total_asset'),'balance-sheet-report.pdf');
return PdfService::render('business::balance-sheets.pdf', compact('asset_datas', 'total_asset', 'duration', 'filter_from_date', 'filter_to_date'),'balance-sheet-report.pdf');
}
}

View File

@@ -90,13 +90,37 @@ public function exportCsv()
return Excel::download(new ExportBillWisePofit, 'bill-wise-profit.csv');
}
public function exportPdf()
public function exportPdf(Request $request)
{
$profits = Sale::with('party:id,name')
->where('business_id', auth()->user()->business_id)
->latest()
->get();
$salesQuery = Sale::with('party:id,name')
->where('business_id', auth()->user()->business_id);
return PdfService::render('business::bill-wise-profits.pdf', compact('profits'),'bill-wise-profit-report.pdf');
$salesQuery->when($request->search, function ($query) use ($request) {
$query->where(function ($q) use ($request) {
$q->where('lossProfit', 'like', '%' . $request->search . '%')
->orWhere('totalAmount', 'like', '%' . $request->search . '%')
->orWhere('invoiceNumber', 'like', '%' . $request->search . '%')
->orWhereHas('party', function ($q) use ($request) {
$q->where('name', 'like', '%' . $request->search . '%');
});
});
});
$salesQuery->when($request->party_id, function ($query, $partyId) {
if ($partyId === 'Guest') {
$query->whereNull('party_id');
} else {
$query->where('party_id', $partyId);
}
});
$duration = $request->custom_days ?: 'today';
[$filter_from_date, $filter_to_date] = $this->applyDateRange($duration, $request->from_date, $request->to_date);
$this->applyDateFilter($salesQuery, $duration, 'saleDate', $request->from_date, $request->to_date);
$profits = $salesQuery->latest()->limit($request->per_page ?? 20)->get();
return PdfService::render('business::bill-wise-profits.pdf', compact('profits', 'duration', 'filter_from_date', 'filter_to_date'),'bill-wise-profit-report.pdf');
}
}

View File

@@ -6,15 +6,21 @@
use App\Models\PaymentType;
use App\Models\Transaction;
use App\Http\Controllers\Controller;
use Carbon\Carbon;
use App\Services\PdfService;
use App\Traits\DateFilterTrait;
use App\Traits\DateRangeTrait;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Business\App\Exports\ExportCashBalance;
class AcnooCashController extends Controller
{
use HasUploader;
use DateFilterTrait, DateRangeTrait;
public function index(Request $request)
{
$business_id = auth()->user()->business_id;
@@ -24,37 +30,16 @@ public function index(Request $request)
->where('business_id', auth()->user()->business_id)
->whereIn('transaction_type', ['cash_payment', 'bank_to_cash', 'cash_to_bank', 'adjust_cash', 'cheque_to_cash']);
// Default to today
$startDate = Carbon::today()->format('Y-m-d');
$endDate = Carbon::today()->format('Y-m-d');
// Date Filter
$duration = $request->custom_days ?: 'today';
[$filter_from_date, $filter_to_date] = $this->applyDateRange($duration, $request->from_date, $request->to_date);
if ($request->custom_days === 'yesterday') {
$startDate = Carbon::yesterday()->format('Y-m-d');
$endDate = Carbon::yesterday()->format('Y-m-d');
} elseif ($request->custom_days === 'last_seven_days') {
$startDate = Carbon::today()->subDays(6)->format('Y-m-d');
} elseif ($request->custom_days === 'last_thirty_days') {
$startDate = Carbon::today()->subDays(29)->format('Y-m-d');
} elseif ($request->custom_days === 'current_month') {
$startDate = Carbon::now()->startOfMonth()->format('Y-m-d');
$endDate = Carbon::now()->endOfMonth()->format('Y-m-d');
} elseif ($request->custom_days === 'last_month') {
$startDate = Carbon::now()->subMonth()->startOfMonth()->format('Y-m-d');
$endDate = Carbon::now()->subMonth()->endOfMonth()->format('Y-m-d');
} elseif ($request->custom_days === 'current_year') {
$startDate = Carbon::now()->startOfYear()->format('Y-m-d');
$endDate = Carbon::now()->endOfYear()->format('Y-m-d');
} elseif ($request->custom_days === 'custom_date' && $request->from_date && $request->to_date) {
$startDate = Carbon::parse($request->from_date)->format('Y-m-d');
$endDate = Carbon::parse($request->to_date)->format('Y-m-d');
}
$cash->whereDate('date', '>=', $startDate)
->whereDate('date', '<=', $endDate);
$this->applyDateFilter($cash, $duration, 'date', $request->from_date, $request->to_date);
$cashes = $cash->when(request('search'), function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('date', 'like', '%' . $request->search . '%')
->orWhere('platform', 'like', '%' . $request->search . '%')
->orWhere('transaction_type', 'like', '%' . $request->search . '%')
->orWhere('amount', 'like', '%' . $request->search . '%')
->orWhereHas('user', function ($q) use ($request) {
@@ -65,13 +50,30 @@ public function index(Request $request)
->latest()
->paginate($request->per_page ?? 20)->appends($request->query());
// Sum of all credits and bank_to_cash
$totalCredit = (clone $cash)->where(function ($q) {
$q->where('type', 'credit')
->orWhere('transaction_type', 'bank_to_cash');
})
->sum('amount');
// Sum of all debits and cash_to_bank
$totalDebit = (clone $cash)->where(function ($q) {
$q->where('type', 'debit')
->orWhere('transaction_type', 'cash_to_bank');
})
->sum('amount');
$cash_balance = $totalCredit - $totalDebit;
if ($request->ajax()) {
return response()->json([
'data' => view('business::cashes.datas', compact('cashes'))->render()
'data' => view('business::cashes.datas', compact('cashes', 'cash_balance', 'duration', 'filter_from_date', 'filter_to_date'))->render(),
'cash_balance' => currency_format($cash_balance, 'icon', 2, business_currency())
]);
}
return view('business::cashes.index', compact('cashes', 'banks'));
return view('business::cashes.index', compact('cashes', 'banks', 'cash_balance', 'duration', 'filter_from_date', 'filter_to_date'));
}
public function store(Request $request)
@@ -282,5 +284,109 @@ public function destroy(string $id)
}
}
public function deleteAll(Request $request)
{
$ids = $request->ids;
DB::beginTransaction();
try {
$transactions = Transaction::whereIn('id', $ids)->get();
foreach ($transactions as $transaction) {
// Allow only "cash" platform transactions
if ($transaction->platform !== 'cash') {
return response()->json([
'message' => 'Cannot delete here, please delete from ' . ucfirst($transaction->platform) . ' section.',
], 400);
}
$amount = $transaction->amount;
$toBank = $transaction->to_bank ? PaymentType::find($transaction->to_bank) : null;
switch ($transaction->transaction_type) {
case 'cash_to_bank':
if ($toBank) {
if ($toBank->balance < $amount) {
return response()->json([
'message' => 'Cannot delete: bank balance would go negative.',
], 400);
}
$toBank->decrement('balance', $amount);
}
break;
case 'adjust_cash':
// Cash is static, so no bank adjustments needed
break;
}
if ($transaction->image && file_exists($transaction->image)) {
Storage::delete($transaction->image);
}
$transaction->delete();
}
DB::commit();
return response()->json([
'message' => __('Cash transactions reversed and deleted successfully.'),
'redirect' => route('business.cashes.index'),
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'message' => 'Error: ' . $e->getMessage(),
], 500);
}
}
public function exportExcel()
{
return Excel::download(new ExportCashBalance, 'cash-balances.xlsx');
}
public function exportCsv()
{
return Excel::download(new ExportCashBalance, 'cash-balances.csv');
}
public function exportPdf(Request $request)
{
$cash = Transaction::with('user:id,name')
->where('business_id', auth()->user()->business_id)
->whereIn('transaction_type', ['cash_payment', 'bank_to_cash', 'cash_to_bank', 'adjust_cash', 'cheque_to_cash']);
// Date Filter
$duration = $request->custom_days ?: 'today';
[$filter_from_date, $filter_to_date] = $this->applyDateRange($duration, $request->from_date, $request->to_date);
$this->applyDateFilter($cash, $duration, 'date', $request->from_date, $request->to_date);
$cashes = $cash->when(request('search'), function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('date', 'like', '%' . $request->search . '%')
->orWhere('platform', 'like', '%' . $request->search . '%')
->orWhere('transaction_type', 'like', '%' . $request->search . '%')
->orWhere('amount', 'like', '%' . $request->search . '%')
->orWhereHas('user', function ($q) use ($request) {
$q->where('name', 'like', '%' . $request->search . '%');
});
});
})
->latest()
->limit($request->per_page ?? 20)->get();
return PdfService::render('business::cashes.pdf', compact('cashes', 'duration', 'filter_from_date', 'filter_to_date'), 'cash-balances.pdf');
}
}

View File

@@ -5,6 +5,7 @@
use App\Models\Product;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Services\PdfService;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Business\App\Exports\ExportComboProduct;
@@ -13,9 +14,14 @@ class AcnooComboProductController extends Controller
public function index(Request $request)
{
$user = auth()->user();
$branchId = null;
if (moduleCheck('MultiBranchAddon')) {
$branchId = $user->branch_id ?? $user->active_branch_id;
}
$search = $request->input('search');
$combo_products = Product::with(['combo_products', 'unit:id,unitName', 'category:id,categoryName'])
$combo_products = Product::with(['combo_products.stock.product', 'unit:id,unitName', 'category:id,categoryName'])
->where('business_id', auth()->user()->business_id)
->where('product_type', 'combo')
->withCount('combo_products as total_combo_products')
@@ -31,6 +37,12 @@ public function index(Request $request)
});
});
})
->when($branchId, function ($q) use ($branchId) {
$q->whereHas('combo_products.stock', function ($q) use ($branchId) {
$q->where('branch_id', $branchId);
});
})
->latest()
->paginate($request->per_page ?? 20)->appends($request->query());
@@ -73,4 +85,55 @@ public function exportCsv()
{
return Excel::download(new ExportComboProduct, 'combo-products.csv');
}
public function exportPdf(Request $request)
{
$user = auth()->user();
$branchId = null;
if (moduleCheck('MultiBranchAddon')) {
$branchId = $user->branch_id ?? $user->active_branch_id;
}
$search = $request->input('search');
$combo_products = Product::with(['combo_products.stock.product', 'unit:id,unitName', 'category:id,categoryName'])
->where('business_id', auth()->user()->business_id)
->where('product_type', 'combo')
->withCount('combo_products as total_combo_products')
->when($search, function ($q) use ($search) {
$q->where(function ($q) use ($search) {
$q->where('productName', 'like', '%' . $search . '%')
->orWhere('productCode', 'like', '%' . $search . '%')
->orWhereHas('category', function ($q) use ($search) {
$q->where('categoryName', 'like', '%' . $search . '%');
})
->orWhereHas('unit', function ($q) use ($search) {
$q->where('unitName', 'like', '%' . $search . '%');
});
});
})
->when($branchId, function ($q) use ($branchId) {
$q->whereHas('combo_products.stock', function ($q) use ($branchId) {
$q->where('branch_id', $branchId);
});
})
->latest()
->limit($request->per_page ?? 20)->get();
$combo_products->transform(function ($product) {
$product->total_stock = $product->combo_products->sum(function ($combo) {
return optional($combo->stock)->productStock ?? 0;
});
$product->total_cost = $product->combo_products->sum(function ($combo) {
return ($combo->quantity ?? 0) * ($combo->purchase_price ?? 0);
});
return $product;
});
return PdfService::render('business::products.combo-products.pdf', compact('combo_products'),'combo-products.pdf');
}
}

View File

@@ -54,7 +54,7 @@ public function index(Request $request)
return view('business::party-reports.customer-ledger.index', compact('customers', 'totalAmount', 'totalPaid', 'totalDue'));
}
public function show(Request $request, $partyId, PartyLedgerService $service)
public function show(Request $request, string $partyId, PartyLedgerService $service)
{
$party = Party::find($partyId);
$ledger = $service->list($request, $partyId);
@@ -78,18 +78,30 @@ public function exportCsv()
return Excel::download(new ExportCustomerLedger, 'customer-ledger.csv');
}
public function exportPdf()
public function exportPdf(Request $request)
{
$customers = Party::with('sales')
->where('business_id', auth()->user()->business_id)
->where('type', '!=', 'Supplier')
->when(request('search'), function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('name', 'like', '%' . $request->search . '%')
->orWhere('phone', 'like', '%' . $request->search . '%')
->orWhere('type', 'like', '%' . $request->search . '%');
});
})
->when(request('type'), function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('type', $request->type);
});
})
->latest()
->get();
->limit($request->per_page ?? 20)->get();
return PdfService::render('business::party-reports.customer-ledger.pdf', compact('customers'),'customer-ledger-report.pdf');
}
public function exportLedgerExcel(Request $request, $partyId, PartyLedgerService $service)
public function exportLedgerExcel(Request $request, string $partyId, PartyLedgerService $service)
{
$party = Party::findOrFail($partyId);
@@ -98,7 +110,7 @@ public function exportLedgerExcel(Request $request, $partyId, PartyLedgerService
return Excel::download(new ExportSingleCustomerLedger($ledger, $party), 'single-customer-ledger.xlsx');
}
public function exportLedgerCsv(Request $request, $partyId, PartyLedgerService $service)
public function exportLedgerCsv(Request $request, string $partyId, PartyLedgerService $service)
{
$party = Party::findOrFail($partyId);
@@ -107,15 +119,18 @@ public function exportLedgerCsv(Request $request, $partyId, PartyLedgerService $
return Excel::download(new ExportSingleCustomerLedger($ledger, $party), 'single-customer-ledger.csv');
}
public function exportLedgerPdf(Request $request, $partyId, PartyLedgerService $service)
public function exportLedgerPdf(Request $request, string $partyId, PartyLedgerService $service)
{
$party = Party::findOrFail($partyId);
$ledger = $service->list($request, $partyId);
return PdfService::render(
'business::party-reports.customer-ledger.show-details.pdf',
compact('ledger', 'party'),
$duration = $request->custom_days ?: $request->duration ?: 'today';
[$fromDate, $toDate] = app(PartyLedgerService::class)->applyDateRange($duration, $request->from_date, $request->to_date);
return PdfService::render('business::party-reports.customer-ledger.show-details.pdf',
compact('ledger', 'party', 'duration', 'fromDate', 'toDate'),
strtolower($party->name).'-ledger.pdf'
);
}

View File

@@ -2,21 +2,25 @@
namespace Modules\Business\App\Http\Controllers;
use App\Services\Invoice\DueInvoiceService;
use Carbon\Carbon;
use App\Models\Sale;
use App\Models\Party;
use App\Models\Business;
use App\Models\Purchase;
use App\Models\DueCollect;
use App\Models\PaymentType;
use App\Models\Transaction;
use App\Services\PdfService;
use Illuminate\Http\Request;
use Barryvdh\DomPDF\Facade\Pdf;
use App\Events\DuePaymentReceived;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Mail;
use App\Events\MultiPaymentProcessed;
use App\Models\Transaction;
use Carbon\Carbon;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Business\App\Exports\ExportDueList;
use Modules\Business\App\Exports\ExportGuestDue;
class AcnooDueController extends Controller
{
@@ -30,29 +34,6 @@ public function index(Request $request)
$businessId = auth()->user()->business_id;
$activeBranch = auth()->user()->active_branch;
if ($activeBranch) {
$total_supplier_due = Party::where('business_id', $businessId)
->where('type', 'Supplier')
->with('purchases_dues')
->get()
->sum(fn($p) => $p->purchases_dues->sum('dueAmount'));
$total_customer_due = Party::where('business_id', $businessId)
->where('type', '!=', 'Supplier')
->with('sales_dues')
->get()
->sum(fn($p) => $p->sales_dues->sum('dueAmount'));
} else {
$total_supplier_due = Party::where('business_id', $businessId)
->where('type', 'Supplier')
->sum('due');
$total_customer_due = Party::where('business_id', $businessId)
->where('type', '!=', 'Supplier')
->sum('due');
}
$query = Party::where('business_id', $businessId)
->with(['purchases_dues', 'sales_dues']);
@@ -69,42 +50,70 @@ public function index(Request $request)
->orWhere('phone', 'like', "%$search%")
->orWhere('email', 'like', "%$search%");
if (!$activeBranch) {
if (! $activeBranch) {
$q->orWhere('due', 'like', "%$search%");
}
});
}
$totalQuery = clone $query;
$parties = $query->latest()->paginate($request->per_page ?? 20)->appends($request->query());
if ($activeBranch) {
$filtered = $parties->getCollection()
->map(function ($party) {
$total_supplier_due = (clone $totalQuery)
->where('type', 'Supplier')
->get()
->sum(fn ($p) => $p->purchases_dues->sum('dueAmount'));
$party->due = $party->type === 'Supplier'
? $party->purchases_dues->sum('dueAmount')
: $party->sales_dues->sum('dueAmount');
$total_customer_due = (clone $totalQuery)
->where('type', '!=', 'Supplier')
->get()
->sum(fn ($p) => $p->sales_dues->sum('dueAmount'));
} else {
$total_supplier_due = (clone $totalQuery)
->where('type', 'Supplier')
->sum('due');
$total_customer_due = (clone $totalQuery)
->where('type', '!=', 'Supplier')
->sum('due');
}
if ($activeBranch) {
$filtered = $parties->getCollection()
->map(function ($party) use ($activeBranch) {
$sale_dues = $party->type === 'Supplier'
? $party->purchases_dues->where('branch_id', $activeBranch->id)
: $party->sales_dues->where('branch_id', $activeBranch->id);
$party_due = $sale_dues->sum('dueAmount');
if ($party->branch_id === $activeBranch->id) {
$openingBalanceDue = $party->opening_balance_type === 'due' ? ($party->opening_balance ?? 0) : 0;
$party->due = $openingBalanceDue + $party_due;
} else {
$party->due = $party_due;
}
return $party;
})
->filter(fn($p) => $p->due > 0)
->filter(fn ($p) => $p->due > 0)
->values();
$parties->setCollection($filtered);
} else {
$parties->setCollection(
$parties->getCollection()->filter(fn($p) => $p->due > 0)->values()
$parties->getCollection()->filter(fn ($p) => $p->due > 0)->values()
);
}
if ($request->ajax()) {
return response()->json([
'data' => view('business::dues.datas', [
'parties' => $parties,
'total_supplier_due' => $total_supplier_due,
'total_customer_due' => $total_customer_due
])->render()
'data' => view('business::dues.datas', compact('parties'))->render(),
'total_supplier_due' => currency_format($total_supplier_due, currency: business_currency()),
'total_customer_due' => currency_format($total_customer_due, currency: business_currency())
]);
}
@@ -115,24 +124,34 @@ public function index(Request $request)
));
}
public function collectDue($id)
public function collectDue(string $id)
{
$activeBranch = auth()->user()->active_branch;
$party = Party::where('business_id', auth()->user()->business_id)->with(['sales_dues', 'purchases_dues'])->findOrFail($id);
$payment_types = PaymentType::where('business_id', auth()->user()->business_id)->whereStatus(1)->latest()->get();
$due_amount = 0;
if ($party->type == 'Supplier') {
foreach ($party->purchases_dues as $sales_due) {
$due_amount += $sales_due->dueAmount;
if (! $activeBranch || $sales_due->branch_id === $activeBranch->id) {
$due_amount += $sales_due->dueAmount;
}
}
} else {
foreach ($party->sales_dues as $sales_due) {
$due_amount += $sales_due->dueAmount;
if (! $activeBranch || $sales_due->branch_id === $activeBranch->id) {
$due_amount += $sales_due->dueAmount;
}
}
}
if (auth()->user()->active_branch) {
$party_opening_due = 0;
if ($activeBranch) {
if ($party->branch_id === $activeBranch->id && $party->opening_balance_type === 'due') {
$party_opening_due = $party->opening_balance ?? 0;
} else {
$party_opening_due = 0;
}
} else {
$party_opening_due = $party->due - $due_amount;
}
@@ -140,28 +159,24 @@ public function collectDue($id)
return view('business::dues.collect-due', compact('party', 'party_opening_due', 'payment_types'));
}
public function collectDueStore(Request $request)
{
$party = Party::find($request->party_id);
$activeBranch = auth()->user()->active_branch;
$request->validate([
'paymentDate' => 'required|string',
'payDueAmount' => 'required|numeric',
'party_id' => 'required|exists:parties,id',
'invoiceNumber' => 'nullable|exists:' . ($party->type == 'Supplier' ? 'purchases' : 'sales') . ',invoiceNumber',
'invoiceNumber' => 'nullable|exists:'.($party->type == 'Supplier' ? 'purchases' : 'sales').',invoiceNumber',
]);
$business_id = auth()->user()->business_id;
if (auth()->user()->active_branch && !$request->invoiceNumber) {
return response()->json([
'message' => __('You must select an invoice when login any branch.')
], 400);
}
$business = Business::findOrFail(auth()->user()->business_id);
DB::beginTransaction();
try {
$branch_id = null;
$branch_id = $activeBranch?->id;
$invoice = null;
$payments = $request->payments ?? [];
@@ -173,81 +188,104 @@ public function collectDueStore(Request $request)
}
$payDueAmount = collect($payments)
->reject(fn($p) => strtolower($p['type']) === 'cheque')
->reject(fn ($p) => strtolower($p['type']) === 'cheque')
->sum('amount');
// Get related invoice if exists
$invoice_due = 0;
$opening_balance_due = 0;
if ($activeBranch) {
if ($party->type == 'Supplier') {
$invoice_due = Purchase::where('party_id', $request->party_id)
->where('branch_id', $activeBranch->id)
->sum('dueAmount');
} else {
$invoice_due = Sale::where('party_id', $request->party_id)
->where('branch_id', $activeBranch->id)
->sum('dueAmount');
}
if ($party->branch_id === $activeBranch->id && $party->opening_balance_type === 'due') {
$opening_balance_due = $party->opening_balance ?? 0;
}
} else {
if ($party->type == 'Supplier') {
$invoice_due = Purchase::where('party_id', $request->party_id)->sum('dueAmount');
} else {
$invoice_due = Sale::where('party_id', $request->party_id)->sum('dueAmount');
}
$opening_balance_due = $party->opening_balance_type === 'due' ? ($party->opening_balance ?? 0) : 0;
}
$total_due = $invoice_due + $opening_balance_due;
if ($payDueAmount > $total_due) {
return response()->json([
'message' => __('You can pay maximum '.$total_due.' due amount.'),
], 400);
}
if ($activeBranch && ! $request->invoiceNumber && $invoice_due > 0 && $opening_balance_due == 0) {
return response()->json([
'message' => __('You must select an invoice when login any branch.'),
], 400);
}
if ($request->invoiceNumber) {
if ($party->type == 'Supplier') {
$invoice = Purchase::where('invoiceNumber', $request->invoiceNumber)->where('party_id', $request->party_id)->first();
$invoice = Purchase::where('invoiceNumber', $request->invoiceNumber)
->where('party_id', $request->party_id)
->where('branch_id', $activeBranch?->id)
->first();
} else {
$invoice = Sale::where('invoiceNumber', $request->invoiceNumber)->where('party_id', $request->party_id)->first();
$invoice = Sale::where('invoiceNumber', $request->invoiceNumber)
->where('party_id', $request->party_id)
->where('branch_id', $activeBranch?->id)
->first();
}
if (!isset($invoice)) {
if (! isset($invoice)) {
return response()->json([
'message' => 'Invoice Not Found.'
'message' => 'Invoice Not Found.',
], 404);
}
if (!auth()->user()->active_branch) {
if (isset($invoice) && isset($invoice->branch_id)) {
$branch_id = $invoice->branch_id;
}
}
if ($invoice->dueAmount < $request->payDueAmount) {
return response()->json([
'message' => 'Invoice due is ' . $invoice->dueAmount . '. You can not pay more then the invoice due amount.'
'message' => 'Invoice due is '.$invoice->dueAmount.'. You can not pay more then the invoice due amount.',
], 400);
}
$branch_id = $invoice->branch_id;
}
// Validation for all invoices due
if (!$request->invoiceNumber) {
if ($party->type == 'Supplier') {
$all_invoice_due = Purchase::where('party_id', $request->party_id)->sum('dueAmount');
} else {
$all_invoice_due = Sale::where('party_id', $request->party_id)->sum('dueAmount');
}
if (($all_invoice_due + $request->payDueAmount) > $party->due) {
return response()->json([
'message' => __('You can pay only ' . $party->due - $all_invoice_due . ', without selecting an invoice.')
], 400);
}
if ($party->opening_balance_type == 'due') {
$party->update([
'opening_balance' => max(0, $party->opening_balance - $payDueAmount),
]);
}
if (! $request->invoiceNumber && $opening_balance_due > 0) {
$party->update([
'opening_balance' => max(0, $party->opening_balance - $payDueAmount),
]);
}
$data = DueCollect::create($request->except('user_id', 'business_id', 'sale_id', 'purchase_id', 'totalDue', 'dueAmountAfterPay', 'paymentDate') + [
'user_id' => auth()->id(),
'business_id' => $business_id,
'sale_id' => $party->type != 'Supplier' && isset($invoice) ? $invoice->id : NULL,
'purchase_id' => $party->type == 'Supplier' && isset($invoice) ? $invoice->id : NULL,
'totalDue' => isset($invoice) ? $invoice->dueAmount : $party->due,
'dueAmountAfterPay'=> isset($invoice) ? ($invoice->dueAmount - $payDueAmount) : ($party->due - $payDueAmount),
'paymentDate' => $request->paymentDate ? Carbon::parse($request->paymentDate)->setTimeFromTimeString(now()->format('H:i:s')) : now()
]);
'business_id' => $business->id,
'sale_id' => $party->type != 'Supplier' && isset($invoice) ? $invoice->id : null,
'purchase_id' => $party->type == 'Supplier' && isset($invoice) ? $invoice->id : null,
'totalDue' => $total_due,
'dueAmountAfterPay' => $total_due - $payDueAmount,
'paymentDate' => $request->paymentDate ? Carbon::parse($request->paymentDate)->setTimeFromTimeString(now()->format('H:i:s')) : now(),
]);
// Update invoice due
if (isset($invoice)) {
$invoice->update([
'dueAmount' => $invoice->dueAmount - $payDueAmount,
'paidAmount' => $invoice->paidAmount + $payDueAmount
'paidAmount' => $invoice->paidAmount + $payDueAmount,
]);
}
// Update party due
$party->update([
'due' => $party->due - $payDueAmount
'due' => $party->due - $payDueAmount,
]);
// update balance & adjust platform
if ($party->type == 'Supplier') {
updateBalance($payDueAmount, 'decrement', $branch_id);
$platform = 'due_pay';
@@ -256,7 +294,6 @@ public function collectDueStore(Request $request)
$platform = 'due_collect';
}
// MultiPaymentProcessed event
event(new MultiPaymentProcessed(
$payments,
$data->id,
@@ -265,45 +302,30 @@ public function collectDueStore(Request $request)
$party->id,
));
// Notify
event(new DuePaymentReceived($data));
sendNotifyToUser($data->id, route('business.dues.index', ['id' => $data->id]), __('Due Collection has been created.'), $business_id);
sendNotifyToUser($data->id, route('business.dues.index', ['id' => $data->id]), __('Due Collection has been created.'), $business->id);
if (moduleCheck('MarketingAddon')) {
$this->dueTransactionMessage($business, $party, $data);
}
DB::commit();
return response()->json([
'message' => __('Collect Due saved successfully'),
'redirect' => route('business.dues.index'),
'secondary_redirect_url' => route('business.collect.dues.invoice', $party->id),
'secondary_redirect_url' => route('business.collect.dues.invoice', $data->id),
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json(['message' => $e->getMessage()], 404);
}
}
public function getInvoice($id)
public function getInvoice($party_id, DueInvoiceService $service)
{
$due_collect = DueCollect::with('user:id,name,role', 'party:id,name,email,phone,type', 'payment_type:id,name', 'business:id,companyName,address,phoneNumber,email,vat_name,vat_no,meta', )
->where('business_id', auth()->user()->business_id)
->where('party_id', $id)
->latest()
->first();
$transactionTypes = $due_collect->transactions
->map(function ($transaction) {
if ($transaction->transaction_type === 'bank_payment' && !empty($transaction->paymentType?->name)) {
return $transaction->paymentType->name;
}
return $transaction->transaction_type ? ucfirst(explode('_', $transaction->transaction_type)[0]) : '';
})
->unique()
->implode(', ');
$party = Party::with('dueCollect.business')->find($id);
$bank_detail = PaymentType::where('business_id', auth()->user()->business_id)->where('show_in_invoice', 1)->latest()->first();
return view('business::dues.invoice', compact('due_collect', 'party', 'transactionTypes', 'bank_detail'));
$data = $service->getInvoiceData($party_id);
return view('business::dues.invoice', $data);
}
public function generatePDF($id)
@@ -391,10 +413,10 @@ public function partyDue(Request $request)
// Apply search
$query->when($request->search, function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('email', 'like', '%' . $request->search . '%')
->orWhere('name', 'like', '%' . $request->search . '%')
->orWhere('phone', 'like', '%' . $request->search . '%')
->orWhere('due', 'like', '%' . $request->search . '%');
$q->where('email', 'like', '%'.$request->search.'%')
->orWhere('name', 'like', '%'.$request->search.'%')
->orWhere('phone', 'like', '%'.$request->search.'%')
->orWhere('due', 'like', '%'.$request->search.'%');
});
});
@@ -402,36 +424,46 @@ public function partyDue(Request $request)
$parties = $query->latest()->paginate($request->per_page ?? 20)->appends($request->query());
if ($activeBranch) {
// Calculate branch-wise due and replace $party->due, remove zero-due parties
$parties->setCollection(
$parties->getCollection()
->transform(function ($party) {
$party->due = $party->type === 'Supplier'
? $party->purchases_dues->sum('dueAmount')
: $party->sales_dues->sum('dueAmount');
->transform(function ($party) use ($activeBranch) {
$sale_dues = $party->type === 'Supplier'
? $party->purchases_dues->where('branch_id', $activeBranch->id)
: $party->sales_dues->where('branch_id', $activeBranch->id);
$party_due = $sale_dues->sum('dueAmount');
if ($party->branch_id === $activeBranch->id) {
$openingBalanceDue = $party->opening_balance_type === 'due' ? ($party->opening_balance ?? 0) : 0;
$party->due = $openingBalanceDue + $party_due;
} else {
$party->due = $party_due;
}
return $party;
})
->filter(fn($party) => $party->due > 0)
->filter(fn ($party) => $party->due > 0)
->values()
);
} else {
// Non-active branch: ensure only parties with due > 0
$parties->setCollection(
$parties->getCollection()
->filter(fn($party) => $party->due > 0)
->filter(fn ($party) => $party->due > 0)
->values()
);
}
if ($request->ajax()) {
return response()->json([
'data' => view('business::dues.party.datas', compact('parties'))->render()
'data' => view('business::dues.party.datas', compact('parties'))->render(),
]);
}
return view('business::dues.party.index', compact('parties', 'party_type'));
}
public function walk_dues(Request $request)
{
$walk_in_customers = Sale::where('business_id', auth()->user()->business_id)
@@ -483,8 +515,8 @@ public function walkDuesGetInvoice(string $id)
$due_collect = DueCollect::with('business:id,companyName,address,phoneNumber,email,vat_name,vat_no,meta', 'user:id,name,role', 'payment_type:id,name')
->where('business_id', auth()->user()->business_id)
->whereNull('party_id')
->latest()
->first();
->where('id', $id) // 🔥 FIX HERE
->firstOrFail();
$transactionTypes = $due_collect->transactions
->map(function ($transaction) {
@@ -496,7 +528,8 @@ public function walkDuesGetInvoice(string $id)
->unique()
->implode(', ');
$walk_in_customer = Sale::with('dueCollect.business')->find($id);
// $walk_in_customer = Sale::with('dueCollect.business')->find($id);
$walk_in_customer = Sale::with('dueCollect.business')->find($due_collect->sale_id);
$bank_detail = PaymentType::where('business_id', auth()->user()->business_id)->where('show_in_invoice', 1)->latest()->first();
@@ -527,7 +560,8 @@ public function collectWalkDueStore(Request $request)
DB::beginTransaction();
try {
$branch_id = null;
$invoice = Sale::where('invoiceNumber', $request->invoiceNumber)->whereNull('party_id')->first();
$invoice = Sale::where('business_id', $business_id)->where('invoiceNumber', $request->invoiceNumber)->whereNull('party_id')->first();
if (!$invoice) {
return response()->json([
'message' => 'Invoice Not Found.'
@@ -565,23 +599,23 @@ public function collectWalkDueStore(Request $request)
'sale_id' => $invoice->id,
'invoiceNumber' => $request->invoiceNumber,
'totalDue' => $invoice->dueAmount,
'dueAmountAfterPay' => $invoice->dueAmount - $request->payDueAmount,
'payDueAmount' => $request->payDueAmount,
'dueAmountAfterPay' => $invoice->dueAmount - $payDueAmount,
'payDueAmount' => $payDueAmount,
'paymentDate' => $request->paymentDate,
]);
$invoice->update([
'dueAmount' => $invoice->dueAmount - $request->payDueAmount
'dueAmount' => $invoice->dueAmount - $payDueAmount
]);
updateBalance($request->payDueAmount, 'increment', $branch_id);
updateBalance($payDueAmount, 'increment', $branch_id);
// MultiPaymentProcessed event
event(new MultiPaymentProcessed(
$request->payments ?? [],
$payments,
$data->id,
'due_collect',
$request->payDueAmount,
$payDueAmount,
));
sendNotifyToUser($data->id, route('business.dues.index', ['id' => $data->id]), __('Due Collection has been created.'), $business_id);
@@ -591,7 +625,7 @@ public function collectWalkDueStore(Request $request)
return response()->json([
'message' => __('Collect Due saved successfully'),
'redirect' => route('business.walk-dues.index'),
'secondary_redirect_url' => route('business.collect.walk-dues.invoice', $invoice->id),
'secondary_redirect_url' => route('business.collect.walk-dues.invoice', $data->id),
]);
} catch (\Exception $e) {
DB::rollBack();
@@ -660,4 +694,95 @@ public function viewDuePayment($id)
]);
}
}
public function exportExcel()
{
return Excel::download(new ExportDueList, 'due-list.xlsx');
}
public function exportCsv()
{
return Excel::download(new ExportDueList, 'due-list.csv');
}
public function exportPdf()
{
$businessId = auth()->user()->business_id;
$activeBranch = auth()->user()->active_branch;
$query = Party::where('business_id', $businessId)
->with(['purchases_dues', 'sales_dues']);
if (request('type')) {
$query->where('type', request('type'));
}
if (request('search')) {
$search = request('search');
$query->where(function ($q) use ($search, $activeBranch) {
$q->where('type', 'like', "%$search%")
->orWhere('name', 'like', "%$search%")
->orWhere('phone', 'like', "%$search%")
->orWhere('email', 'like', "%$search%");
if (!$activeBranch) {
$q->orWhere('due', 'like', "%$search%");
}
});
}
$parties = $query->latest()->limit(request('per_page') ?? 20)->get();
if ($activeBranch) {
$parties = $parties->map(function ($party) use ($activeBranch) {
$sale_dues = $party->type === 'Supplier'
? $party->purchases_dues->where('branch_id', $activeBranch->id)
: $party->sales_dues->where('branch_id', $activeBranch->id);
$party_due = $sale_dues->sum('dueAmount');
if ($party->branch_id === $activeBranch->id) {
$openingBalanceDue = $party->opening_balance_type === 'due' ? ($party->opening_balance ?? 0) : 0;
$party->due = $openingBalanceDue + $party_due;
} else {
$party->due = $party_due;
}
return $party;
})->filter(fn($p) => $p->due > 0)->values();
} else {
$parties = $parties->filter(fn($p) => $p->due > 0)->values();
}
return PdfService::render('business::dues.pdf-export', compact('parties'),'due-list.pdf');
}
public function walkDueExportExcel()
{
return Excel::download(new ExportGuestDue, 'guest-due-list.xlsx');
}
public function walkDueExportCsv()
{
return Excel::download(new ExportGuestDue, 'guest-due-list.csv');
}
public function walkDueExportPdf(Request $request)
{
$walk_in_customers = Sale::where('business_id', auth()->user()->business_id)
->with('dueCollect')
->whereNull('party_id')
->where('dueAmount', '>', 0)
->when(request('search'), function ($query) {
$query->where(function ($q) {
$q->where('invoiceNumber', 'like', '%' . request('search') . '%')
->orwhere('dueAmount', 'like', '%' . request('search') . '%');
});
})
->latest()
->limit($request->per_page ?? 20)->get();
return PdfService::render('business::walk-dues.pdf', compact('walk_in_customers'),'guest-due-list.pdf');
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Modules\Business\App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class AcnooEstimateController extends Controller
{
public function index(Request $request)
{
return view('business::estimates.index');
}
public function create()
{
return view('business::estimates.create');
}
public function store()
{
return view('business::estimates.create');
}
}

View File

@@ -5,6 +5,7 @@
use App\Models\Product;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Services\PdfService;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Business\App\Exports\ExportExpiredProduct;
@@ -64,4 +65,38 @@ public function exportCsv()
{
return Excel::download(new ExportExpiredProduct, 'expired-products.csv');
}
public function exportPdf(Request $request)
{
$expired_products = Product::with('unit:id,unitName', 'brand:id,brandName', 'category:id,categoryName', 'stocks')
->where('business_id', auth()->user()->business_id)
->withSum('stocks as total_stock', 'productStock')
->whereHas('stocks', function ($query) {
$query->whereDate('expire_date', '<', today())
->where('productStock', '>', 0);
})
->when($request->search, function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('type', 'like', '%' . $request->search . '%')
->orWhere('productName', 'like', '%' . $request->search . '%')
->orWhere('productCode', 'like', '%' . $request->search . '%')
->orWhere('hsn_code', 'like', '%' . $request->search . '%')
->orWhere('productSalePrice', 'like', '%' . $request->search . '%')
->orWhere('productPurchasePrice', 'like', '%' . $request->search . '%')
->orWhereHas('unit', function ($q) use ($request) {
$q->where('unitName', 'like', '%' . $request->search . '%');
})
->orWhereHas('brand', function ($q) use ($request) {
$q->where('brandName', 'like', '%' . $request->search . '%');
})
->orWhereHas('category', function ($q) use ($request) {
$q->where('categoryName', 'like', '%' . $request->search . '%');
});
});
})
->latest()
->limit($request->per_page ?? 20)->get();
return PdfService::render('business::expired-products.pdf', compact('expired_products'),'expired-products.pdf');
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Modules\Business\App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\State;
use Illuminate\Http\Request;
class AcnooGetStateController extends Controller
{
public function index(Request $request)
{
$states = State::where('country_id', $request->country_id)->where('status', 1)->orderBy('name')->get(['id', 'name']);
return response()->json([
'status' => true,
'states' => $states
]);
}
}

View File

@@ -28,15 +28,15 @@ public function index(Request $request)
$salesQuery = DB::table('sales')
->select(
DB::raw('DATE(saleDate) as date'),
DB::raw('DATE("saleDate") as date'),
DB::raw('SUM(actual_total_amount) as total_sales'),
DB::raw('SUM(lossProfit) as total_sale_income')
DB::raw('SUM("lossProfit") as total_sale_income')
)
->where('business_id', $businessId)
->when($branchId, fn ($q) =>
$q->where('branch_id', $branchId)
)
->groupBy(DB::raw('DATE(saleDate)'));
->groupBy(DB::raw('DATE("saleDate")'));
$this->applyDateFilter($salesQuery, $duration, 'saleDate', $request->from_date, $request->to_date);
$dailySales = $salesQuery->get();
@@ -50,14 +50,14 @@ public function index(Request $request)
$incomeQuery = DB::table('incomes')
->select(
DB::raw('DATE(incomeDate) as date'),
DB::raw('DATE("incomeDate") as date'),
DB::raw('SUM(amount) as total_incomes')
)
->where('business_id', $businessId)
->when($branchId, fn ($q) =>
$q->where('branch_id', $branchId)
)
->groupBy(DB::raw('DATE(incomeDate)'));
->groupBy(DB::raw('DATE("incomeDate")'));
$this->applyDateFilter($incomeQuery, $duration, 'incomeDate', $request->from_date, $request->to_date);
$dailyIncomes = $incomeQuery->get();
@@ -90,14 +90,14 @@ public function index(Request $request)
if (moduleCheck('HrmAddon')) {
$payrollQuery = DB::table('payrolls')
->select(
DB::raw('DATE(date) as date'),
DB::raw('DATE("date") as date'),
DB::raw('SUM(amount) as total_payrolls')
)
->where('business_id', $businessId)
->when($branchId, fn ($q) =>
$q->where('branch_id', $branchId)
)
->groupBy(DB::raw('DATE(date)'));
->groupBy(DB::raw('DATE("date")'));
$this->applyDateFilter($payrollQuery, $duration, 'date', $request->from_date, $request->to_date);
$dailyPayrolls = $payrollQuery->get();
@@ -105,14 +105,14 @@ public function index(Request $request)
$expenseQuery = DB::table('expenses')
->select(
DB::raw('DATE(expenseDate) as date'),
DB::raw('DATE("expenseDate") as date'),
DB::raw('SUM(amount) as total_expenses_only')
)
->where('business_id', $businessId)
->when($branchId, fn ($q) =>
$q->where('branch_id', $branchId)
)
->groupBy(DB::raw('DATE(expenseDate)'));
->groupBy(DB::raw('DATE("expenseDate")'));
$this->applyDateFilter($expenseQuery, $duration, 'expenseDate', $request->from_date, $request->to_date);
$dailyExpenses = $expenseQuery->get();
@@ -212,9 +212,9 @@ public function exportPdf()
// SALES
$dailySales = DB::table('sales')
->select(
DB::raw('DATE(saleDate) as date'),
DB::raw('DATE("saleDate") as date'),
DB::raw('SUM(actual_total_amount) as total_sales'),
DB::raw('SUM(lossProfit) as total_sale_income')
DB::raw('SUM("lossProfit") as total_sale_income')
)
->where('business_id', $businessId)
->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
@@ -231,7 +231,7 @@ public function exportPdf()
// INCOME
$dailyIncomes = DB::table('incomes')
->select(
DB::raw('DATE(incomeDate) as date'),
DB::raw('DATE("incomeDate") as date'),
DB::raw('SUM(amount) as total_incomes')
)
->where('business_id', $businessId)
@@ -262,7 +262,7 @@ public function exportPdf()
if (moduleCheck('HrmAddon')) {
$dailyPayrolls = DB::table('payrolls')
->select(
DB::raw('DATE(date) as date'),
DB::raw('DATE("date") as date'),
DB::raw('SUM(amount) as total_payrolls')
)
->where('business_id', $businessId)
@@ -274,7 +274,7 @@ public function exportPdf()
// EXPENSES
$dailyExpenses = DB::table('expenses')
->select(
DB::raw('DATE(expenseDate) as date'),
DB::raw('DATE("expenseDate") as date'),
DB::raw('SUM(amount) as total_expenses_only')
)
->where('business_id', $businessId)

View File

@@ -25,15 +25,15 @@ public function index(Request $request)
$salesQuery = DB::table('sales')
->select(
DB::raw('DATE(saleDate) as date'),
DB::raw('DATE("saleDate") as date'),
DB::raw('SUM(actual_total_amount) as total_sales'),
DB::raw('SUM(lossProfit) as total_sale_income')
DB::raw('SUM("lossProfit") as total_sale_income')
)
->where('business_id', $businessId)
->when($branchId, fn ($q) =>
$q->where('branch_id', $branchId)
)
->groupBy(DB::raw('DATE(saleDate)'));
->groupBy(DB::raw('DATE("saleDate")'));
$this->applyDateFilter($salesQuery, $duration, 'saleDate', $request->from_date, $request->to_date);
$dailySales = $salesQuery->get();
@@ -47,14 +47,14 @@ public function index(Request $request)
$incomeQuery = DB::table('incomes')
->select(
DB::raw('DATE(incomeDate) as date'),
DB::raw('DATE("incomeDate") as date'),
DB::raw('SUM(amount) as total_incomes')
)
->where('business_id', $businessId)
->when($branchId, fn ($q) =>
$q->where('branch_id', $branchId)
)
->groupBy(DB::raw('DATE(incomeDate)'));
->groupBy(DB::raw('DATE("incomeDate")'));
$this->applyDateFilter($incomeQuery, $duration, 'incomeDate', $request->from_date, $request->to_date);
$dailyIncomes = $incomeQuery->get();
@@ -87,14 +87,14 @@ public function index(Request $request)
if (moduleCheck('HrmAddon')) {
$payrollQuery = DB::table('payrolls')
->select(
DB::raw('DATE(date) as date'),
DB::raw('DATE("date") as date'),
DB::raw('SUM(amount) as total_payrolls')
)
->where('business_id', $businessId)
->when($branchId, fn ($q) =>
$q->where('branch_id', $branchId)
)
->groupBy(DB::raw('DATE(date)'));
->groupBy(DB::raw('DATE("date")'));
$this->applyDateFilter($payrollQuery, $duration, 'date', $request->from_date, $request->to_date);
$dailyPayrolls = $payrollQuery->get();
@@ -102,14 +102,14 @@ public function index(Request $request)
$expenseQuery = DB::table('expenses')
->select(
DB::raw('DATE(expenseDate) as date'),
DB::raw('DATE("expenseDate") as date'),
DB::raw('SUM(amount) as total_expenses_only')
)
->where('business_id', $businessId)
->when($branchId, fn ($q) =>
$q->where('branch_id', $branchId)
)
->groupBy(DB::raw('DATE(expenseDate)'));
->groupBy(DB::raw('DATE("expenseDate")'));
$this->applyDateFilter($expenseQuery, $duration, 'expenseDate', $request->from_date, $request->to_date);
$dailyExpenses = $expenseQuery->get();

View File

@@ -3,11 +3,18 @@
namespace Modules\Business\App\Http\Controllers;
use App\Models\Party;
use App\Models\PaymentType;
use App\Helpers\HasUploader;
use App\Imports\PartyImport;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Maatwebsite\Excel\Facades\Excel;
use App\Events\MultiPaymentProcessed;
// use App\Models\Country;
// use App\Models\State;
use Illuminate\Support\Facades\Storage;
class AcnooPartyController extends Controller
{
@@ -30,16 +37,16 @@ public function index(Request $request)
$party_type = $request->input('type');
$query = Party::where('business_id', $business_id)
->when($search, function ($q) use ($search) {
$q->where(function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%')
->orWhere('credit_limit', 'like', '%' . $search . '%')
->orWhere('phone', 'like', '%' . $search . '%')
->orWhere('type', 'like', '%' . $search . '%')
->orWhere('address', 'like', '%' . $search . '%')
->orWhere('due', 'like', '%' . $search . '%');
});
->when($search, function ($q) use ($search) {
$q->where(function ($q) use ($search) {
$q->where('name', 'like', '%'.$search.'%')
->orWhere('credit_limit', 'like', '%'.$search.'%')
->orWhere('phone', 'like', '%'.$search.'%')
->orWhere('type', 'like', '%'.$search.'%')
->orWhere('address', 'like', '%'.$search.'%')
->orWhere('due', 'like', '%'.$search.'%');
});
});
// Filter by party type
if ($party_type === 'Customer') {
@@ -54,11 +61,19 @@ public function index(Request $request)
$parties->setCollection(
$parties->getCollection()
->transform(function ($party) use ($activeBranch) {
$party_due = $party->type === 'Supplier'
? $party->purchases_dues->sum('dueAmount')
: $party->sales_dues->sum('dueAmount');
$originalDue = $party->getRawOriginal('due') ?? 0;
$originalOpeningBalance = $party->opening_balance ?? 0;
$party->due = $party->branch_id === $activeBranch->id ? $party_due + $party->due : $party_due;
$party_due = $party->type === 'Supplier'
? $party->purchases_dues->where('branch_id', $activeBranch->id)->sum('dueAmount')
: $party->sales_dues->where('branch_id', $activeBranch->id)->sum('dueAmount');
if ($party->branch_id === $activeBranch->id) {
$openingBalanceDue = $party->opening_balance_type === 'due' ? $originalOpeningBalance : 0;
$party->due = $openingBalanceDue + $party_due;
} else {
$party->due = $party_due;
}
return $party;
})
@@ -67,16 +82,20 @@ public function index(Request $request)
if ($request->ajax()) {
return response()->json([
'data' => view('business::parties.datas', compact('parties'))->render()
'data' => view('business::parties.datas', compact('parties'))->render(),
]);
}
return view('business::parties.index', compact('parties', 'party_type'));
}
public function create()
{
return view('business::parties.create');
$countries = [];
$states = [];
return view('business::parties.create', compact('countries', 'states'));
}
public function store(Request $request)
@@ -88,13 +107,12 @@ public function store(Request $request)
'email' => 'nullable|email',
'image' => 'nullable|image|mimes:jpeg,png,jpg,svg',
'address' => 'nullable|string|max:255',
'tax_no' => 'nullable|string|max:255',
'due' => 'nullable|numeric|min:0',
'billing_address' => 'nullable|array',
'billing_address.address' => 'nullable|string|max:255',
'billing_address.city' => 'nullable|string|max:255',
'billing_address.state' => 'nullable|string|max:255',
'billing_address.zip_code' => 'nullable|string|max:20',
'billing_address.country' => 'nullable|string|max:255',
'shipping_address' => 'nullable|array',
'shipping_address.address' => 'nullable|string|max:255',
'shipping_address.city' => 'nullable|string|max:255',
@@ -127,7 +145,10 @@ public function store(Request $request)
public function edit($id)
{
$party = Party::where('business_id', auth()->user()->business_id)->findOrFail($id);
return view('business::parties.edit', compact('party'));
$countries = [];
$states = [];
return view('business::parties.edit', compact('party', 'countries', 'states'));
}
public function update(Request $request, $id)
@@ -141,13 +162,12 @@ public function update(Request $request, $id)
'email' => 'nullable|email',
'image' => 'nullable|image|mimes:jpeg,png,jpg,svg',
'address' => 'nullable|string|max:255',
'tax_no' => 'nullable|string|max:255',
'due' => 'nullable|numeric|min:0',
'billing_address' => 'nullable|array',
'billing_address.address' => 'nullable|string|max:255',
'billing_address.city' => 'nullable|string|max:255',
'billing_address.state' => 'nullable|string|max:255',
'billing_address.zip_code' => 'nullable|string|max:20',
'billing_address.country' => 'nullable|string|max:255',
'shipping_address' => 'nullable|array',
'shipping_address.address' => 'nullable|string|max:255',
'shipping_address.city' => 'nullable|string|max:255',
@@ -210,6 +230,79 @@ public function update(Request $request, $id)
]);
}
public function advancePayment(string $id)
{
$party = Party::where('business_id', auth()->user()->business_id)->with(['sales_dues', 'purchases_dues'])->findOrFail($id);
$payment_types = PaymentType::where('business_id', auth()->user()->business_id)->whereStatus(1)->latest()->get();
return view('business::parties.advance-payment', compact('party', 'payment_types'));
}
public function advancePaymentStore(Request $request)
{
$request->validate([
'party_id' => 'required|exists:parties,id',
'amount' => 'required|numeric|min:0.01',
'date' => 'nullable|date',
'note' => 'nullable|string|max:255',
]);
DB::beginTransaction();
try {
$party = Party::findOrFail($request->party_id);
$payments = $request->payments ?? [];
if (isset($payments['main'])) {
$mainPayment = $payments['main'];
$mainPayment['amount'] = $request->amount ?? 0;
$payments = [$mainPayment];
}
// Only count actual received money (exclude cheque)
$payAmount = collect($payments)
->reject(fn($p) => strtolower($p['type'] ?? '') === 'cheque')
->sum(fn($p) => $p['amount'] ?? 0);
if ($payAmount <= 0) {
return response()->json([
'message' => __('No valid payment amount received (cheques are pending).')
], 400);
}
// Determine platform based on party type
$platform = $party->type === 'Supplier' ? 'advance_pay' : 'advance_collect';
$party->increment('wallet', $payAmount);
event(new MultiPaymentProcessed(
$payments,
$party->id,
$platform,
$payAmount,
$party->id,
$request->date ?? now(),
$request->note ?? null
));
DB::commit();
$type = in_array($party->type, ['Retailer', 'Dealer', 'Wholesaler']) ? 'Customer' : ($party->type === 'Supplier' ? 'Supplier' : '');
$message = $type === 'Supplier' ? __('Advance amount paid successfully.') : __('Advance amount collected successfully.');
return response()->json([
'message' => $message,
'redirect' => route('business.parties.index', ['type' => $type])
]);
} catch (\Exception $e) {
DB::rollback();
return response()->json([
'error' => 'Transaction failed: ' . $e->getMessage()
], 500);
}
}
public function destroy($id)
{
$party = Party::findOrFail($id);
@@ -266,4 +359,28 @@ public function deleteAll(Request $request)
]);
}
public function bulkStore(Request $request, $type)
{
$request->validate([
'file' => 'required|file|mimes:xlsx,xls,csv'
]);
$businessId = auth()->user()->business_id;
$import = new PartyImport($businessId);
try {
Excel::import($import, $request->file('file'));
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 422);
}
return response()->json([
'success' => true,
'message' => __('Bulk upload successfully.'),
'redirect' => route('business.parties.index', ['type' => $type])
]);
}
}

View File

@@ -24,7 +24,6 @@
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Facades\Storage;
use Modules\Business\App\Exports\ExportProduct;
use Modules\Business\App\Exports\ExportExpiredProduct;
class AcnooProductController extends Controller
{
@@ -40,7 +39,7 @@ public function __construct()
public function index(Request $request)
{
$user = auth()->user();
$user = auth()->user();
$search = $request->input('search');
$products = Product::query()
@@ -66,13 +65,14 @@ public function index(Request $request)
->when($search, function ($q) use ($search) {
$q->where(function ($q) use ($search) {
$q->where('productName', 'like', "%{$search}%")
->orWhere('productCode', 'like', "%{$search}%")
->orWhere('productPurchasePrice', 'like', "%{$search}%")
->orWhere('productSalePrice', 'like', "%{$search}%")
->orWhere('product_type', 'like', "%{$search}%")
->orWhereHas('category', fn($q) => $q->where('categoryName', 'like', "%{$search}%"))
->orWhereHas('brand', fn($q) => $q->where('brandName', 'like', "%{$search}%"))
->orWhereHas('unit', fn($q) => $q->where('unitName', 'like', "%{$search}%"));
->orWhere('productCode', 'like', "%{$search}%")
->orWhere('hsn_code', 'like', "%{$search}%")
->orWhere('productPurchasePrice', 'like', "%{$search}%")
->orWhere('productSalePrice', 'like', "%{$search}%")
->orWhere('product_type', 'like', "%{$search}%")
->orWhereHas('category', fn ($q) => $q->where('categoryName', 'like', "%{$search}%"))
->orWhereHas('brand', fn ($q) => $q->where('brandName', 'like', "%{$search}%"))
->orWhereHas('unit', fn ($q) => $q->where('unitName', 'like', "%{$search}%"));
});
})
->latest()
@@ -82,22 +82,22 @@ public function index(Request $request)
$products->getCollection()->transform(function ($product) {
if ($product->product_type === 'combo') {
$product->total_stock = $product->combo_products->sum(
fn($combo) => $combo->stock?->productStock ?? 0
fn ($combo) => $combo->stock?->productStock ?? 0
);
$product->total_cost = $product->combo_products->sum(
fn($combo) => ($combo->quantity ?? 0) * ($combo->purchase_price ?? 0)
fn ($combo) => ($combo->quantity ?? 0) * ($combo->purchase_price ?? 0)
);
$product->combo_items = $product->combo_products->map(function ($combo) {
return [
'name' => $combo->stock?->product?->productName ?? 'N/A',
'quantity' => $combo->quantity ?? 0,
'name' => $combo->stock?->product?->productName ?? 'N/A',
'quantity' => $combo->quantity ?? 0,
'purchase_price' => currency_format(
($combo->purchase_price ?? 0) * ($combo->quantity ?? 0),
currency: business_currency()
),
'stock' => $combo->stock?->productStock ?? 0,
'stock' => $combo->stock?->productStock ?? 0,
];
});
}
@@ -114,7 +114,6 @@ public function index(Request $request)
return view('business::products.index', compact('products'));
}
public function create()
{
$business_id = auth()->user()->business_id;
@@ -122,7 +121,7 @@ public function create()
$brands = Brand::where('business_id', $business_id)->whereStatus(1)->latest()->get();
$units = Unit::where('business_id', $business_id)->whereStatus(1)->latest()->get();
$product_id = (Product::where('business_id', $business_id)->count() ?? 0) + 1;
$vats = Vat::where('business_id', $business_id)->latest()->get();
$vats = Vat::whereStatus(1)->where('business_id', $business_id)->latest()->get();
$code = str_pad($product_id, 4, '0', STR_PAD_LEFT);
$product_models = ProductModel::where('business_id', $business_id)->latest()->get();
$warehouses = Warehouse::where('business_id', $business_id)->latest()->get();
@@ -130,9 +129,10 @@ public function create()
$racks = Rack::where('business_id', $business_id)->latest()->get();
$shelves = Shelf::where('business_id', $business_id)->latest()->get();
$profit_option = Option::where('key', 'business-settings')
->where('value', 'LIKE', '%"business_id":%' . $business_id . '%')
->where('value', 'LIKE', '%"business_id":' . $business_id . '%')
->get()
->firstWhere('value.business_id', $business_id)['product_profit_option'] ?? '';
->firstWhere('value.business_id', $business_id)
->value['product_profit_option'] ?? '';
return view('business::products.create', compact('categories', 'brands', 'units', 'code', 'vats', 'product_models', 'warehouses', 'racks', 'shelves', 'profit_option', 'variations'));
}
@@ -159,6 +159,7 @@ public function store(Request $request)
return $query->where('business_id', $business_id);
}),
],
'hsn_code' => 'nullable|string|max:255',
'alert_qty' => 'nullable|numeric|min:0',
'size' => 'nullable|string|max:255',
'type' => 'nullable|string|max:255',
@@ -206,13 +207,21 @@ function ($attribute, $value, $fail) use ($request) {
$vat = Vat::find($request->vat_id);
$vat_rate = $vat->rate ?? 0;
$combo_tax_inclusive = $request->productSalePrice ?? 0;
$combo_tax_exclusive = $request->productSalePrice ?? 0;
if ($request->vat_id && $request->product_type === 'combo') {
$combo_tax_inclusive = number_format($combo_tax_exclusive * (1 + ($vat_rate / 100)), 3, '.', '');
}
// Create the product
$product = Product::create($request->only('productName', 'unit_id', 'brand_id', 'vat_id', 'vat_type', 'category_id', 'productCode', 'product_type', 'rack_id', 'shelf_id', 'model_id', 'variation_ids', 'warranty_guarantee_info') + [
$product = Product::create($request->only('productName', 'unit_id', 'brand_id', 'vat_id', 'vat_type', 'category_id', 'productCode', 'hsn_code', 'product_type', 'rack_id', 'shelf_id', 'model_id', 'variation_ids', 'warranty_guarantee_info') + [
'business_id' => $business_id,
'alert_qty' => $request->alert_qty ?? 0,
'is_displayed_in_pos' => $request->has('is_displayed_in_pos') ? 1 : 0,
'has_serial' => $request->product_type == 'combo' ? 0 : ($request->has_serial ?? 0),
'profit_percent' => $request->product_type == 'combo' ? $request->profit_percent ?? 0 : 0,
'productSalePrice' => $request->product_type == 'combo' ? $request->productSalePrice ?? 0 : 0,
'productSalePrice' => $request->product_type == 'combo' ? $combo_tax_inclusive : 0, // inclusive sales price
'price_without_tax' => $request->product_type == 'combo' ? $combo_tax_exclusive : 0,
'productPicture' => $request->productPicture ? $this->upload($request, 'productPicture') : NULL,
]);
@@ -220,10 +229,6 @@ function ($attribute, $value, $fail) use ($request) {
if (in_array($request->product_type, ['single', 'variant']) && !empty($request->stocks)) {
$stockData = [];
foreach ($request->stocks as $stock) {
$base_price = $stock['exclusive_price'] ?? 0;
$purchasePrice = $request->vat_type === 'inclusive'
? $base_price + ($base_price * $vat_rate / 100)
: $base_price;
$stockData[] = [
'business_id' => $business_id,
@@ -231,7 +236,7 @@ function ($attribute, $value, $fail) use ($request) {
'batch_no' => $stock['batch_no'] ?? null,
'warehouse_id' => $stock['warehouse_id'] ?? null,
'productStock' => $stock['productStock'] ?? 0,
'productPurchasePrice' => $purchasePrice,
'productPurchasePrice' => $stock['inclusive_price'] ?? 0, // inclusive purchase price / net cost price
'profit_percent' => $stock['profit_percent'] ?? 0,
'productSalePrice' => $stock['productSalePrice'] ?? 0,
'productWholeSalePrice' => $stock['productWholeSalePrice'] ?? 0,
@@ -281,16 +286,17 @@ public function edit($id)
$categories = Category::where('business_id', $business_id)->whereStatus(1)->latest()->get();
$brands = Brand::where('business_id', $business_id)->whereStatus(1)->latest()->get();
$units = Unit::where('business_id', $business_id)->whereStatus(1)->latest()->get();
$vats = Vat::where('business_id', $business_id)->latest()->get();
$vats = Vat::whereStatus(1)->where('business_id', $business_id)->latest()->get();
$product_models = ProductModel::where('business_id', $business_id)->latest()->get();
$warehouses = Warehouse::where('business_id', $business_id)->latest()->get();
$racks = Rack::where('business_id', $business_id)->latest()->get();
$shelves = Shelf::where('business_id', $business_id)->latest()->get();
$variations = Variation::where('business_id', auth()->user()->business_id)->where('status', 1)->get();
$profit_option = Option::where('key', 'business-settings')
->where('value', 'LIKE', '%"business_id":%' . $business_id . '%')
->where('value', 'LIKE', '%"business_id":' . $business_id . '%')
->get()
->firstWhere('value.business_id', $business_id)['product_profit_option'] ?? '';
->firstWhere('value.business_id', $business_id)
->value['product_profit_option'] ?? '';
$product = Product::with([
'stocks' => function ($query) {
@@ -334,6 +340,7 @@ public function update(Request $request, $id)
return $query->where('business_id', $business_id);
}),
],
'hsn_code' => 'nullable|string|max:255',
'alert_qty' => 'nullable|numeric|min:0',
'size' => 'nullable|string|max:255',
'type' => 'nullable|string|max:255',
@@ -381,12 +388,21 @@ function ($attribute, $value, $fail) use ($request) {
$vat = Vat::find($request->vat_id);
$vat_rate = $vat->rate ?? 0;
$combo_tax_exclusive = $request->productSalePrice ?? 0;
$combo_tax_inclusive = $request->productSalePrice ?? 0;
if ($request->vat_id && $request->product_type === 'combo') {
$combo_tax_inclusive = number_format($combo_tax_exclusive * (1 + ($vat_rate / 100)), 3, '.', '');
}
// Update product
$product->update($request->except(['productPicture', 'productPurchasePrice', 'productDealerPrice', 'productWholeSalePrice', 'alert_qty', 'stocks', 'vat_amount', 'profit_percent', 'is_displayed_in_pos']) + [
$product->update($request->except(['productPicture', 'productPurchasePrice', 'productDealerPrice', 'productWholeSalePrice', 'alert_qty', 'stocks', 'vat_amount', 'profit_percent', 'productSalePrice', 'price_without_tax']) + [
'business_id' => $business_id,
'alert_qty' => $request->alert_qty ?? 0,
'is_displayed_in_pos' => $request->has('is_displayed_in_pos') ? 1 : 0,
'profit_percent' => $request->profit_percent ?? 0,
'has_serial' => $request->product_type == 'combo' ? 0 : ($request->has_serial ?? 0),
'profit_percent' => $request->product_type == 'combo' ? $request->profit_percent ?? 0 : 0,
'productSalePrice' => $request->product_type == 'combo' ? $combo_tax_inclusive : 0, // inclusive sales price
'price_without_tax' => $request->product_type == 'combo' ? $combo_tax_exclusive : 0,
'productPicture' => $request->productPicture ? $this->upload($request, 'productPicture', $product->productPicture) : $product->productPicture,
]);
@@ -406,22 +422,15 @@ function ($attribute, $value, $fail) use ($request) {
// Insert or Update
foreach ($request->stocks as $stock) {
$stockId = $stock['stock_id'] ?? null;
// Recalculate price
$base_price = $stock['exclusive_price'] ?? 0;
$purchasePrice = $request->vat_type === 'inclusive'
? $base_price + ($base_price * $vat_rate / 100)
: $base_price;
$payload = [
'business_id' => $business_id,
'product_id' => $product->id,
'batch_no' => $stock['batch_no'] ?? null,
'warehouse_id' => $stock['warehouse_id'] ?? null,
'productStock' => $stock['productStock'] ?? 0,
'productPurchasePrice' => $purchasePrice,
'productPurchasePrice' =>$stock['inclusive_price'] ?? 0,
'profit_percent' => $stock['profit_percent'] ?? 0,
'productSalePrice' => $stock['productSalePrice'] ?? 0,
'productWholeSalePrice' => $stock['productWholeSalePrice'] ?? 0,
@@ -431,7 +440,7 @@ function ($attribute, $value, $fail) use ($request) {
'variation_data' => $stock['variation_data'] ?? null,
'variant_name' => $stock['variant_name'] ?? null,
'branch_id' => auth()->user()->branch_id ?? auth()->user()->active_branch_id,
'serial_numbers' => $request->has_serial ? $stock['serial_numbers'] : null,
'serial_numbers' => $request->has_serial ? $stock['serial_numbers'] ?? null : null,
];
if ($stockId) {
@@ -463,13 +472,12 @@ function ($attribute, $value, $fail) use ($request) {
} catch (\Exception $e) {
DB::rollback();
return response()->json([
'message' => __('Something went wrong.'),
'error' => $e->getMessage(),
'message' => $e->getMessage(),
], 406);
}
}
public function destroy($id)
public function destroy(string $id)
{
$product = Product::findOrFail($id);
if (file_exists($product->productPicture)) {
@@ -494,33 +502,47 @@ public function deleteAll(Request $request)
}
Product::whereIn('id', $request->ids)->delete();
return response()->json([
'message' => __('Selected product deleted successfully'),
'redirect' => route('business.products.index')
'message' => __('Selected product deleted successfully'),
'redirect' => route('business.products.index')
]);
}
public function getAllProduct()
{
$isTransfer = request()->is_transfer == 1;
$products = Product::with([
'stocks' => function ($query) {
'stocks' => function ($query) use ($isTransfer) {
$query->where('productStock', '>', 0);
if (!$isTransfer) {
$query->where(function ($q) {
$q->whereNull('serial_numbers')
->orWhereJsonLength('serial_numbers', 0);
});
}
},
'category:id,categoryName',
'unit:id,unitName',
'stocks.warehouse:id,name',
'vat:id,rate'
'stocks.warehouse:id,name'
])
->where('business_id', auth()->user()->business_id)
->withSum('stocks as total_stock', 'productStock')
->withSum(['stocks as total_stock' => function ($query) use ($isTransfer) {
$query->where('productStock', '>', 0);
if (!$isTransfer) {
$query->where(function ($q) {
$q->whereNull('serial_numbers')
->orWhereJsonLength('serial_numbers', 0);
});
}
}], 'productStock')
->having('total_stock', '>', 0)
->latest()
->get()
->where('total_stock', '>', 0)
->values();
->get();
return response()->json($products);
}
public function getByCategory($category_id)
public function getByCategory(string $category_id)
{
$products = Product::where('business_id', auth()->user()->business_id)->where('category_id', $category_id)->get();
return response()->json($products);
@@ -536,15 +558,63 @@ public function exportCsv()
return Excel::download(new ExportProduct, 'product.csv');
}
public function exportPdf()
public function exportPdf(Request $request)
{
$products = Product::with('unit:id,unitName', 'brand:id,brandName', 'category:id,categoryName')
->where('business_id', auth()->user()->business_id)
$user = auth()->user();
$search = $request->input('search');
$products = Product::query()
->where('business_id', $user->business_id)
->with([
'stocks',
'unit:id,unitName',
'brand:id,brandName',
'category:id,categoryName',
'warehouse:id,name',
'rack:id,name',
'shelf:id,name',
'combo_products.stock.product:id,productName'
])
->withSum('stocks as total_stock', 'productStock')
->where(function ($query) {
$query->where('product_type', '!=', 'combo')
->orWhere(function ($q) {
$q->where('product_type', 'combo')
->whereHas('combo_products');
});
})
->when($search, function ($q) use ($search) {
$q->where(function ($q) use ($search) {
$q->where('productName', 'like', "%{$search}%")
->orWhere('productCode', 'like', "%{$search}%")
->orWhere('hsn_code', 'like', "%{$search}%")
->orWhere('productPurchasePrice', 'like', "%{$search}%")
->orWhere('productSalePrice', 'like', "%{$search}%")
->orWhere('product_type', 'like', "%{$search}%")
->orWhereHas('category', fn ($q) => $q->where('categoryName', 'like', "%{$search}%"))
->orWhereHas('brand', fn ($q) => $q->where('brandName', 'like', "%{$search}%"))
->orWhereHas('unit', fn ($q) => $q->where('unitName', 'like', "%{$search}%"));
});
})
->latest()
->limit($request->per_page ?? 20)
->get();
return PdfService::render('business::products.pdf', compact('products'), 'product-list.pdf');
$products = $products->transform(function ($product) {
if ($product->product_type === 'combo') {
$product->total_stock = $product->combo_products->sum(
fn ($combo) => $combo->stock?->productStock ?? 0
);
$product->total_cost = $product->combo_products->sum(
fn ($combo) => ($combo->quantity ?? 0) * ($combo->purchase_price ?? 0)
);
}
return $product;
});
return PdfService::render('business::products.pdf', compact('products'),'product-list.pdf');
}
public function expiredProduct(Request $request)
@@ -561,6 +631,7 @@ public function expiredProduct(Request $request)
$q->where('type', 'like', '%' . $request->search . '%')
->orWhere('productName', 'like', '%' . $request->search . '%')
->orWhere('productCode', 'like', '%' . $request->search . '%')
->orWhere('hsn_code', 'like', '%' . $request->search . '%')
->orWhere('productSalePrice', 'like', '%' . $request->search . '%')
->orWhere('productPurchasePrice', 'like', '%' . $request->search . '%')
->orWhereHas('unit', function ($q) use ($request) {
@@ -586,103 +657,23 @@ public function expiredProduct(Request $request)
return view('business::expired-products.index', compact('expired_products'));
}
public function exportExpireProductExcel()
{
return Excel::download(new ExportExpiredProduct, 'expired-product.xlsx');
}
public function exportExpireProductCsv()
{
return Excel::download(new ExportExpiredProduct, 'expired-product.csv');
}
public function show($id)
public function show(string $id)
{
$business_id = auth()->user()->business_id;
$product = Product::with('stocks')->where('business_id', $business_id)->findOrFail($id);
$categories = Category::where('business_id', $business_id)->whereStatus(1)->latest()->get();
$brands = Brand::where('business_id', $business_id)->whereStatus(1)->latest()->get();
$units = Unit::where('business_id', $business_id)->whereStatus(1)->latest()->get();
$vats = Vat::where('business_id', $business_id)->latest()->get();
$vats = Vat::whereStatus(1)->where('business_id', $business_id)->latest()->get();
$profit_option = Option::where('key', 'business-settings')
->where('value', 'LIKE', '%"business_id":%' . $business_id . '%')
->where('value', 'LIKE', '%"business_id":' . $business_id . '%')
->get()
->firstWhere('value.business_id', $business_id)['product_profit_option'] ?? '';
->firstWhere('value.business_id', $business_id)
->value['product_profit_option'] ?? '';
return view('business::products.create-stock', compact('categories', 'brands', 'units', 'product', 'vats', 'profit_option'));
}
public function CreateStock(Request $request, string $id)
{
$product = Product::findOrFail($id);
$business_id = auth()->user()->business_id;
$request->validate([
'vat_id' => 'nullable|exists:vats,id',
'vat_type' => 'nullable|in:inclusive,exclusive',
'productDealerPrice' => 'nullable|numeric|min:0',
'exclusive_price' => 'required|numeric|min:0',
'inclusive_price' => 'required|numeric|min:0',
'profit_percent' => 'nullable|numeric',
'productSalePrice' => 'required|numeric|min:0',
'productWholeSalePrice' => 'nullable|numeric|min:0',
'productStock' => 'required|numeric|min:0',
'expire_date' => 'nullable|date',
'batch_no' => 'nullable|string',
'productCode' => [
'nullable',
'unique:products,productCode,' . $product->id . ',id,business_id,' . $business_id,
],
]);
DB::beginTransaction();
try {
// Calculate purchase price including VAT if applicable
$vat = Vat::find($request->vat_id);
$exclusive_price = $request->exclusive_price ?? 0;
$vat_amount = ($exclusive_price * ($vat->rate ?? 0)) / 100;
// Determine final purchase price based on VAT type
$purchase_price = $request->vat_type === 'exclusive' ? $exclusive_price : $exclusive_price + $vat_amount;
$batchNo = $request->batch_no ?? null;
$stock = Stock::where(['batch_no' => $batchNo, 'product_id' => $product->id])->first();
if ($stock) {
$stock->update($request->except('productStock', 'productPurchasePrice', 'productSalePrice', 'productDealerPrice', 'productWholeSalePrice') + [
'productStock' => $stock->productStock + $request->productStock,
'productPurchasePrice' => $purchase_price,
'productSalePrice' => $request->productSalePrice,
'productDealerPrice' => $request->productDealerPrice ?? 0,
'productWholeSalePrice' => $request->productWholeSalePrice ?? 0,
]);
} else {
Stock::create($request->except('productStock', 'productPurchasePrice', 'productSalePrice', 'productDealerPrice', 'productWholeSalePrice') + [
'product_id' => $product->id,
'branch_id' => auth()->user()->branch_id ?? auth()->user()->active_branch_id,
'business_id' => $business_id,
'productStock' => $request->productStock ?? 0,
'productPurchasePrice' => $purchase_price,
'productSalePrice' => $request->productSalePrice,
'productDealerPrice' => $request->productDealerPrice ?? 0,
'productWholeSalePrice' => $request->productWholeSalePrice ?? 0,
]);
}
DB::commit();
return response()->json([
'message' => __('Data saved successfully.'),
'redirect' => route('business.products.index'),
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'message' => __('Something went wrong.'),
], 406);
}
}
public function getShelf(Request $request)
{
$rack = Rack::with('shelves')->find($request->rack_id);
@@ -698,4 +689,53 @@ public function getProductVariants($product_id)
return response()->json($variant_stocks);
}
public function checkSerial(Request $request)
{
$request->validate([
'product_id' => 'required|exists:products,id',
'serial' => 'required|string'
]);
$exists = Stock::where('business_id', auth()->user()->business_id)
->where('product_id', $request->product_id)
->whereNotNull('serial_numbers')
->whereJsonContains('serial_numbers', $request->serial)
->exists();
return response()->json([
'exists' => $exists
]);
}
public function getProductSerials(Request $request)
{
$request->validate([
'product_id' => 'required|exists:products,id',
'warehouse_id' => 'nullable|exists:warehouses,id',
'batch_no' => 'nullable|string',
]);
$business_id = auth()->user()->business_id;
// Get all serial_numbers from stocks
$serials = Stock::where('business_id', $business_id)
->where('product_id', $request->product_id)
->when($request->filled('batch_no'), function ($q) use ($request) {
$q->where('batch_no', $request->batch_no);
})
->when($request->filled('warehouse_id'), function ($q) use ($request) {
$q->where('warehouse_id', $request->warehouse_id);
})
->whereNotNull('serial_numbers')
->pluck('serial_numbers')
->flatten()
->values();
return response()->json([
'success' => true,
'serials' => $serials
]);
}
}

View File

@@ -45,8 +45,8 @@ public function index(Request $request)
->with('product:id,productName,productCode')
->select(
'product_id',
DB::raw('SUM(CASE WHEN lossProfit > 0 THEN lossProfit ELSE 0 END) AS profit'),
DB::raw('SUM(CASE WHEN lossProfit < 0 THEN lossProfit ELSE 0 END) AS loss')
DB::raw('SUM(CASE WHEN "lossProfit" > 0 THEN "lossProfit" ELSE 0 END) AS profit'),
DB::raw('SUM(CASE WHEN "lossProfit" < 0 THEN "lossProfit" ELSE 0 END) AS loss')
)
->groupBy('product_id')
->paginate($request->per_page ?? 20)
@@ -93,8 +93,8 @@ public function exportPdf()
})
->select(
'product_id',
DB::raw('SUM(CASE WHEN lossProfit > 0 THEN lossProfit ELSE 0 END) as profit'),
DB::raw('SUM(CASE WHEN lossProfit < 0 THEN lossProfit ELSE 0 END) as loss')
DB::raw('SUM(CASE WHEN "lossProfit" > 0 THEN "lossProfit" ELSE 0 END) as profit'),
DB::raw('SUM(CASE WHEN "lossProfit" < 0 THEN "lossProfit" ELSE 0 END) as loss')
)
->groupBy('product_id')
->get();

View File

@@ -2,10 +2,10 @@
namespace Modules\Business\App\Http\Controllers;
use App\Models\Option;
use Illuminate\Http\Request;
use App\Models\ProductSetting;
use App\Http\Controllers\Controller;
use App\Models\Option;
use App\Models\ProductSetting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Validation\ValidationException;
@@ -14,23 +14,29 @@ class AcnooSettingsManagerController extends Controller
public function index()
{
$businessId = auth()->user()->business_id;
$invoiceSettingKey = 'invoice_setting_' . $businessId;
$invoiceSettingKey = 'invoice_setting_'.$businessId;
$invoice_setting = Option::where('key', $invoiceSettingKey)->first();
$product_setting = ProductSetting::where('business_id', auth()->user()->business_id)->first();
$currencySettingKey = 'currency_setting_' . $businessId;
$currencySettingKey = 'currency_setting_'.$businessId;
$currency_setting = Option::where('key', $currencySettingKey)->first();
return view('business::manage-settings.index', compact('invoice_setting', 'product_setting', 'currency_setting'));
$invoiceImgaeSettingKey = 'invoice_setting_images';
$invoice_setting_image = Option::where('key', $invoiceImgaeSettingKey)->first();
$themesettingkey = 'theme_setting_'.$businessId;
$theme_settings = Option::where('key', $themesettingkey)->first()?->value ?? [];
return view('business::manage-settings.index', compact('invoice_setting', 'theme_settings', 'product_setting', 'currency_setting', 'invoice_setting_image'));
}
public function updateInvoice(Request $request)
{
$request->validate([
'invoice_size' => 'required|string|max:100|in:a4,3_inch_80mm',
'invoice_size' => 'required|string|max:100|in:a4,3_inch_80mm,standard_a4',
]);
$key = 'invoice_setting_' . auth()->user()->business_id;
$key = 'invoice_setting_'.auth()->user()->business_id;
Option::updateOrCreate(
['key' => $key],
@@ -45,8 +51,8 @@ public function updateInvoice(Request $request)
public function updateProductSetting(Request $request)
{
$request->validate([
'show_product_price' => 'nullable|boolean',
'show_product_code' => 'nullable|boolean',
'show_hsn_code' => 'nullable|boolean',
'show_product_stock' => 'nullable|boolean',
'show_product_sale_price' => 'nullable|boolean',
'show_product_dealer_price' => 'nullable|boolean',
@@ -94,9 +100,9 @@ public function updateProductSetting(Request $request)
]);
if (
!$request->boolean('show_product_type_single') &&
!$request->boolean('show_product_type_variant') &&
!$request->boolean('show_product_type_combo')
! $request->boolean('show_product_type_single') &&
! $request->boolean('show_product_type_variant') &&
! $request->boolean('show_product_type_combo')
) {
throw ValidationException::withMessages([
'product_type' => ['At least one product type must be selected: Single, Variant or Combo.'],
@@ -133,7 +139,7 @@ public function updateProductSetting(Request $request)
['modules' => $modules]
);
Cache::forget('product_setting_' . $businessId);
Cache::forget('product_setting_'.$businessId);
return response()->json(__('Product setting updated successfully.'));
}
@@ -144,7 +150,7 @@ public function updateCurrency(Request $request)
'currency' => 'required|string|max:100|in:us,european',
]);
$key = 'currency_setting_' . auth()->user()->business_id;
$key = 'currency_setting_'.auth()->user()->business_id;
Option::updateOrCreate(
['key' => $key],
@@ -174,13 +180,39 @@ public function updateSerial(Request $request)
$modules['show_serial'] = $request->boolean('show_serial') ? '1' : '0';
$setting->update([
'modules' => $modules
'modules' => $modules,
]);
Cache::forget('product_setting_' . $businessId);
Cache::forget('product_setting_'.$businessId);
return response()->json([
'message' => 'Serial setting updated successfully.',
]);
}
public function updateTheme(Request $request)
{
$request->validate([
'primary_color' => 'nullable|string|max:100',
'secondary_color' => 'nullable|string|max:100',
'sidebar_color' => 'nullable|string|max:100',
'accent_color' => 'nullable|string|max:100',
]);
$businessId = auth()->user()->business_id;
$setting = Option::updateOrCreate(
['key' => 'theme_setting_'.$businessId],
['value' => [
'primary_color' => $request->primary_color,
'secondary_color' => $request->secondary_color,
'sidebar_color' => $request->sidebar_color,
'accent_color' => $request->accent_color,
]]
);
Cache::forget('theme_setting_'.$businessId);
return response()->json(__('Theme updated successfully.'));
}
}

View File

@@ -2,13 +2,10 @@
namespace Modules\Business\App\Http\Controllers;
use App\Models\Stock;
use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use App\Services\PdfService;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Pagination\LengthAwarePaginator;
use Modules\Business\App\Exports\ExportCurrentStock;
class AcnooStockController extends Controller
@@ -25,81 +22,130 @@ public function index()
$search_term = request('search');
$per_page = request('per_page', 20);
// Base query
$query = Product::with('stocks','warehouse:id,name', 'rack:id,name', 'shelf:id,name')->where('product_type', '!=', 'combo')->where('business_id', $businessId);
$query = Product::with('stocks', 'category:id,categoryName')
->where('product_type', '!=', 'combo')
->where('business_id', $businessId);
// For Low Stock view - apply search if search term exists
if ($alert_qty_filter) {
// Apply search filter if search term exists
if ($search_term) {
$query->where(function ($q) use ($search_term) {
$q->where('productName', 'like', '%' . $search_term . '%')
->orWhere('productPurchasePrice', 'like', '%' . $search_term . '%')
->orWhere('productSalePrice', 'like', '%' . $search_term . '%');
});
}
// Get all products (with search applied if any) then filter for low stock
$allProducts = $query->latest()->get();
$filteredProducts = $allProducts->filter(function ($product) {
$totalStock = $product->stocks->sum('productStock');
return $totalStock <= $product->alert_qty;
if ($search_term) {
$query->where(function ($q) use ($search_term) {
$q->where('productName', 'like', '%' . $search_term . '%')
->orWhere('productCode', 'like', '%' . $search_term . '%')
->orWhere('hsn_code', 'like', '%' . $search_term . '%')
->orWhereHas('category', function ($q) use ($search_term) {
$q->where('categoryName', 'like', '%' . $search_term . '%');
});
});
}
// Manual pagination for filtered collection
$currentPage = request('page', 1);
$products = new LengthAwarePaginator(
$filteredProducts->forPage($currentPage, $per_page),
$filteredProducts->count(),
$per_page,
$currentPage,
['path' => request()->url(), 'query' => request()->query()]
);
if ($alert_qty_filter) {
$products = $query
->withSum('stocks as total_stock', 'productStock')
->whereRaw('(SELECT COALESCE(SUM("productStock"),0) FROM stocks WHERE stocks.product_id = products.id) <= products.alert_qty')
->latest()
->paginate($per_page)
->appends(request()->query());
// Calculate totals for low stock view
$total_stock_value = $filteredProducts->sum(function ($product) {
$total_stock_qty = $products->sum('total_stock');
$total_stock_value = $products->sum(function ($product) {
return $product->stocks->sum(function ($stock) {
return $stock->productStock * $stock->productPurchasePrice;
});
});
$total_qty = $filteredProducts->sum(function ($product) {
return $product->stocks->sum('productStock');
});
} else {
// For All Stock view - NO search, just regular pagination
$products = $query->latest()->paginate($per_page)->appends(request()->query());
$products = $query
->withSum('stocks as total_stock', 'productStock')
->latest()
->paginate($per_page)
->appends(request()->query());
// Calculate totals for all stock
$total_stock_value = Stock::whereHas('product', function ($q) use ($businessId) {
$q->where('business_id', $businessId);
})->sum(DB::raw('productPurchasePrice * productStock'));
$total_stock_qty = $products->sum('total_stock');
$total_qty = Stock::whereHas('product', function ($q) use ($businessId) {
$q->where('business_id', $businessId);
})->sum('productStock');
$total_stock_value = $products->sum(function ($product) {
return $product->stocks->sum(function ($stock) {
return $stock->productStock * $stock->productPurchasePrice;
});
});
}
// Handle AJAX request
if (request()->ajax()) {
return response()->json([
'data' => view('business::stocks.datas', compact('products', 'total_stock_value', 'total_qty'))->render()
'data' => view('business::stocks.datas', compact('products', 'total_stock_value', 'total_stock_qty'))->render(),
'total_stock_qty' => $total_stock_qty,
'total_stock_value' => currency_format($total_stock_value, currency: business_currency())
]);
}
return view('business::stocks.index', [
'products' => $products,
'total_stock_value' => $total_stock_value,
'total_qty' => $total_qty,
'total_stock_qty' => $total_stock_qty,
]);
}
public function exportExcel()
{
return Excel::download(new ExportCurrentStock, 'current-stock.xlsx');
return Excel::download(new ExportCurrentStock, 'stock-list.xlsx');
}
public function exportCsv()
{
return Excel::download(new ExportCurrentStock, 'current-stock.csv');
return Excel::download(new ExportCurrentStock, 'stock-list.csv');
}
public function exportPdf()
{
$businessId = auth()->user()->business_id;
$alert_qty_filter = request('alert_qty');
$search_term = request('search');
$per_page = request('per_page', 20);
$query = Product::with('stocks', 'category:id,categoryName')
->where('product_type', '!=', 'combo')
->where('business_id', $businessId);
if ($search_term) {
$query->where(function ($q) use ($search_term) {
$q->where('productName', 'like', '%' . $search_term . '%')
->orWhere('productCode', 'like', '%' . $search_term . '%')
->orWhere('hsn_code', 'like', '%' . $search_term . '%')
->orWhereHas('category', function ($q) use ($search_term) {
$q->where('categoryName', 'like', '%' . $search_term . '%');
});
});
}
if ($alert_qty_filter) {
$products = $query
->withSum('stocks as total_stock', 'productStock')
->havingRaw('total_stock <= products.alert_qty')
->latest()
->limit($per_page)
->get();
$total_stock_qty = $products->sum('total_stock');
$total_stock_value = $products->sum(function ($product) {
return $product->stocks->sum(function ($stock) {
return $stock->productStock * $stock->productPurchasePrice;
});
});
} else {
$products = $query
->withSum('stocks as total_stock', 'productStock')
->latest()
->limit($per_page)
->get();
$total_stock_qty = $products->sum('total_stock');
$total_stock_value = $products->sum(function ($product) {
return $product->stocks->sum(function ($stock) {
return $stock->productStock * $stock->productPurchasePrice;
});
});
}
return PdfService::render('business::stocks.pdf', compact('products', 'total_stock_qty', 'total_stock_value'), 'stock-list.pdf');
}
}

View File

@@ -24,7 +24,7 @@ public function index(Request $request)
$total_stock_value = Stock::whereHas('product', function ($q) use ($businessId) {
$q->where('business_id', $businessId);
})->sum(DB::raw('productPurchasePrice * productStock'));
})->sum(DB::raw('"productPurchasePrice" * "productStock"'));
$total_qty = Stock::whereHas('product', function ($q) use ($businessId) {
$q->where('business_id', $businessId);

View File

@@ -50,7 +50,7 @@ public function index(Request $request)
}
public function show(Request $request, $partyId, PartyLedgerService $service)
public function show(Request $request, string $partyId, PartyLedgerService $service)
{
$party = Party::find($partyId);
$ledger = $service->list($request, $partyId);
@@ -74,18 +74,25 @@ public function exportCsv()
return Excel::download(new ExportSupplierLedger, 'supplier-ledger.csv');
}
public function exportPdf()
public function exportPdf(Request $request)
{
$suppliers = Party::with('purchases')
->where('business_id', auth()->user()->business_id)
->where('type', '=', 'Supplier')
->when(request('search'), function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('name', 'like', '%' . $request->search . '%')
->orWhere('phone', 'like', '%' . $request->search . '%')
->orWhere('type', 'like', '%' . $request->search . '%');
});
})
->latest()
->get();
->limit($request->per_page ?? 20)->get();
return PdfService::render('business::party-reports.supplier-ledger.pdf', compact('suppliers'),'supplier-ledger-report.pdf');
}
public function exportLedgerExcel(Request $request, $partyId, PartyLedgerService $service)
public function exportLedgerExcel(Request $request, string $partyId, PartyLedgerService $service)
{
$party = Party::findOrFail($partyId);
$ledger = $service->list($request, $partyId);
@@ -93,7 +100,7 @@ public function exportLedgerExcel(Request $request, $partyId, PartyLedgerService
return Excel::download(new ExportSingleSupplierLedger($ledger, $party), 'single-supplier-ledger.xlsx');
}
public function exportLedgerCsv(Request $request, $partyId, PartyLedgerService $service)
public function exportLedgerCsv(Request $request, string $partyId, PartyLedgerService $service)
{
$party = Party::findOrFail($partyId);
$ledger = $service->list($request, $partyId);
@@ -101,14 +108,18 @@ public function exportLedgerCsv(Request $request, $partyId, PartyLedgerService $
return Excel::download(new ExportSingleSupplierLedger($ledger, $party), 'single-supplier-ledger.csv');
}
public function exportLedgerPdf(Request $request, $partyId, PartyLedgerService $service)
public function exportLedgerPdf(Request $request, string $partyId, PartyLedgerService $service)
{
$party = Party::find($partyId);
$ledger = $service->list($request, $partyId);
$duration = $request->custom_days ?: $request->duration ?: 'today';
[$fromDate, $toDate] = app(PartyLedgerService::class)->applyDateRange($duration, $request->from_date, $request->to_date);
return PdfService::render(
'business::party-reports.supplier-ledger.show-details.pdf',
compact('ledger', 'party'),
compact('ledger', 'party', 'duration', 'fromDate', 'toDate'),
strtolower($party->name).'-supplier.pdf'
);
}

View File

@@ -56,16 +56,29 @@ public function exportCsv()
return Excel::download(new ExportTopCustomer, 'top-customers.csv');
}
public function exportPdf()
public function exportPdf(Request $request)
{
$customers = Party::with('sales')
->where('business_id', auth()->user()->business_id)
->where('type', '!=', 'Supplier')
->whereHas('sales')
->when(request('search'), function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('name', 'like', '%' . $request->search . '%')
->orWhere('phone', 'like', '%' . $request->search . '%')
->orWhere('email', 'like', '%' . $request->search . '%')
->orWhere('type', 'like', '%' . $request->search . '%');
});
})
->withCount('sales')
->withSum('sales', 'totalAmount')
->orderByDesc('sales_count')
->orderByDesc('sales_sum_total_amount')
->when(request('type'), function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('type', $request->type);
});
})
->take(5)
->get();

View File

@@ -51,12 +51,20 @@ public function exportCsv()
return Excel::download(new ExportTopSupplier, 'top-suppliers.csv');
}
public function exportPdf()
public function exportPdf(Request $request)
{
$suppliers = Party::with('purchases')
->where('business_id', auth()->user()->business_id)
->where('type', '=', 'Supplier')
->whereHas('purchases')
->when(request('search'), function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('name', 'like', '%' . $request->search . '%')
->orWhere('phone', 'like', '%' . $request->search . '%')
->orWhere('email', 'like', '%' . $request->search . '%')
->orWhere('type', 'like', '%' . $request->search . '%');
});
})
->withCount('purchases')
->withSum('purchases', 'totalAmount')
->orderByDesc('purchases_count')

View File

@@ -124,13 +124,78 @@ public function exportCsv()
return Excel::download(new ExportTransactionReport, 'bill-wise-profit.csv');
}
public function exportPdf()
public function exportPdf(Request $request)
{
$transactions = Transaction::with('paymentType')
->where('business_id', auth()->user()->business_id)
->latest()
->get();
$businessId = auth()->user()->business_id;
return PdfService::render('business::transactions.pdf', compact('transactions'),'transactions-report.pdf');
$transactionsQuery = Transaction::with('paymentType')
->where('business_id', $businessId);
$duration = $request->custom_days ?: 'today';
[$filter_from_date, $filter_to_date] = $this->applyDateRange($duration, $request->from_date, $request->to_date);
$this->applyDateFilter($transactionsQuery, $duration, 'date', $request->from_date, $request->to_date);
// Search filter
if ($request->filled('search')) {
$transactionsQuery->where(function ($query) use ($request) {
$query->where('amount', 'like', '%' . $request->search . '%')
->orWhere('invoice_no', 'like', '%' . $request->search . '%')
->orWhere('platform', 'like', '%' . $request->search . '%')
->orWhere('type', 'like', '%' . $request->search . '%');
});
}
// Platform filter
if ($request->filled('platform')) {
$transactionsQuery->where('platform', $request->platform);
}
// Party Filter
if ($request->filled('party_id')) {
$transactionsQuery->where(function ($q) use ($request) {
$q->where(function ($saleQ) use ($request) {
$saleQ->where('platform', 'sale')
->whereHas('sale', function ($s) use ($request) {
$s->where('party_id', $request->party_id);
});
});
$q->where(function ($saleRtnQ) use ($request) {
$saleRtnQ->where('platform', 'sale_return')
->whereHas('saleReturn.sale', function ($s) use ($request) {
$s->where('party_id', $request->party_id);
});
});
$q->orWhere(function ($purQ) use ($request) {
$purQ->where('platform', 'purchase')
->whereHas('purchase', function ($p) use ($request) {
$p->where('party_id', $request->party_id);
});
});
$q->orWhere(function ($purRtnQ) use ($request) {
$purRtnQ->where('platform', 'purchase_return')
->whereHas('purchaseReturn.purchase', function ($p) use ($request) {
$p->where('party_id', $request->party_id);
});
});
$q->orWhere(function ($dueQ) use ($request) {
$dueQ->where('platform', 'due_collect')
->whereHas('dueCollect', function ($d) use ($request) {
$d->where('party_id', $request->party_id);
});
});
});
}
$transactions = $transactionsQuery->latest()->limit($request->per_page ?? 20)->get();
return PdfService::render('business::transactions.pdf', compact('transactions', 'duration', 'filter_from_date', 'filter_to_date'),'transactions-report.pdf');
}
}

View File

@@ -10,6 +10,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use App\Services\PdfService;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Business\App\Exports\ExportTransfer;
@@ -25,11 +26,20 @@ public function __construct()
public function index(Request $request)
{
$branches = Branch::withTrashed()->where('business_id', auth()->user()->business_id)->latest()->get();
$user = auth()->user();
$activeBranchId = $user->active_branch_id;
$branches = Branch::withTrashed()->where('business_id', $user->business_id)->latest()->get();
$transfers = Transfer::with(['fromWarehouse:id,name', 'toWarehouse:id,name', 'toBranch:id,name', 'fromBranch:id,name', 'transferProducts'])
->where('business_id', auth()->user()->business_id)
->when($request->branch_id, function ($q) use ($request) {
->where('business_id', $user->business_id)
->when($activeBranchId, function ($q) use ($activeBranchId) {
$q->where(function ($query) use ($activeBranchId) {
$query->where('from_branch_id', $activeBranchId)
->orWhere('to_branch_id', $activeBranchId);
});
})
->when($request->branch_id && !$activeBranchId, function ($q) use ($request) {
$q->where(function ($query) use ($request) {
$query->where('from_branch_id', $request->branch_id)->orWhere('to_branch_id', $request->branch_id);
});
@@ -77,6 +87,19 @@ public function create()
public function store(Request $request)
{
// Normalize serial_numbers
if ($request->has('products')) {
$products = $request->products;
foreach ($products as $key => $item) {
if (!empty($item['serial_numbers']) && is_string($item['serial_numbers'])) {
$products[$key]['serial_numbers'] = json_decode($item['serial_numbers'], true);
}
}
$request->merge(['products' => $products]);
}
$request->validate([
'from_branch_id' => 'nullable|exists:branches,id',
'to_branch_id' => 'nullable|exists:branches,id|required_with:from_branch_id',
@@ -91,6 +114,7 @@ public function store(Request $request)
'products.*.unit_price' => 'required|numeric|min:0',
'products.*.discount' => 'nullable|numeric|min:0',
'products.*.tax' => 'nullable|numeric|min:0',
'products.*.serial_numbers' => 'nullable|array',
]);
$user = auth()->user();
@@ -99,72 +123,54 @@ public function store(Request $request)
$fromWh = $request->from_warehouse_id;
$toWh = $request->to_warehouse_id;
// Transfer validation logic
// Transfer validation
if ($user->active_branch_id && $toBranch && $toWh) {
return response()->json([
'message' => 'You cannot transfer to another branch warehouse.'
], 400);
return response()->json(['message' => 'You cannot transfer to another branch warehouse.'], 400);
}
if (!$toBranch && !$toWh) {
// to_branch or to_warehouse no one exist
return response()->json([
'message' => 'Invalid transfer request. Please select a destination branch or warehouse.'
], 400);
return response()->json(['message' => 'Please select a destination branch or warehouse.'], 400);
}
if ($fromBranch && !$fromWh) {
// Branch to Branch transfer only
if ($fromBranch == $toBranch) {
return response()->json([
'message' => 'Transfer not allowed: Same branch transfer is not possible.'
], 400);
}
} elseif (!$fromBranch && $fromWh) {
// Warehouse to Warehouse transfer only
if ($fromWh == $toWh) {
return response()->json([
'message' => 'Transfer not allowed: Same warehouse transfer is not possible.'
], 400);
}
} elseif ($fromBranch && $fromWh) {
// Both branch and warehouse present
if ($fromBranch == $toBranch && $fromWh == $toWh) {
return response()->json([
'message' => 'Transfer not allowed: Same warehouse transfer within the same branch.'
], 400);
}
} else {
return response()->json([
'message' => 'Invalid transfer request. Please provide branch or warehouse information.'
], 400);
if ($fromBranch && !$fromWh && $fromBranch == $toBranch) {
return response()->json(['message' => 'Same branch transfer is not allowed.'], 400);
}
if ($fromWh && $fromWh == $toWh) {
return response()->json(['message' => 'Same warehouse transfer is not allowed.'], 400);
}
DB::beginTransaction();
try {
$subTotal = 0;
$totalDiscount = 0;
$totalTax = 0;
$subTotal = $totalDiscount = $totalTax = 0;
foreach ($request->products as $item) {
$qty = $item['quantity'];
$price = $item['unit_price'];
$discount = $item['discount'] ?? 0;
$tax = $item['tax'] ?? 0;
// products re-arrange
$products = collect($request->products)->map(function ($item, $key) {
return array_merge($item, ['stock_id' => $item['stock_id'] ?? $key]);
});
$subTotal += ($qty * $price);
$totalTax += $tax;
$totalDiscount += $discount;
// Calculate subtotal using serial count if available
foreach ($products as $item) {
$serials = !empty($item['serial_numbers'])
? (is_array($item['serial_numbers']) ? $item['serial_numbers'] : json_decode($item['serial_numbers'], true))
: [];
$qty = !empty($serials) ? count($serials) : $item['quantity'];
$subTotal += $qty * $item['unit_price'];
$totalTax += $item['tax'] ?? 0;
$totalDiscount += $item['discount'] ?? 0;
}
$shipping = $request->shipping_charge ?? 0;
$grandTotal = $subTotal + $totalTax - $totalDiscount + $shipping;
$transfer = Transfer::create($request->except('business_id', 'shipping_charge', 'sub_total', 'total_discount', 'total_tax', 'grand_total', 'from_warehouse_id', 'to_warehouse_id', 'to_branch_id', 'from_branch_id') + [
'business_id' => auth()->user()->business_id,
$transfer = Transfer::create([
'business_id' => $user->business_id,
'user_id' => auth()->id(),
'from_branch_id' => $fromBranch,
'from_warehouse_id' => $fromWh,
'to_branch_id' => $toBranch,
'to_warehouse_id' => $toWh,
'transfer_date' => $request->transfer_date,
'status' => $request->status,
'note' => $request->note,
'shipping_charge' => $shipping,
'sub_total' => $subTotal,
'total_discount' => $totalDiscount,
@@ -172,115 +178,119 @@ public function store(Request $request)
'grand_total' => $grandTotal,
]);
$transferProductData = [];
foreach ($request->products as $stockId => $item) {
// Find product_id from stock_id
$stock = Stock::find($stockId);
$transferProducts = [];
foreach ($products as $item) {
$stock = Stock::find($item['stock_id']);
if (!$stock) {
return response()->json([
'message' => "Invalid stock ID: {$stockId}"
], 400);
return response()->json(['message' => "Invalid stock ID: {$item['stock_id']}"], 400);
}
$transferProductData[] = [
// Ensure serial_numbers is array
$serials = !empty($item['serial_numbers'])
? (is_array($item['serial_numbers']) ? $item['serial_numbers'] : json_decode($item['serial_numbers'], true))
: [];
$quantity = !empty($serials) ? count($serials) : $item['quantity'];
$transferProducts[] = [
'transfer_id' => $transfer->id,
'stock_id' => $stockId,
'stock_id' => $stock->id,
'product_id' => $stock->product_id,
'quantity' => $item['quantity'] ?? 0,
'unit_price' => $item['unit_price'] ?? 0,
'quantity' => $quantity,
'unit_price' => $item['unit_price'],
'discount' => $item['discount'] ?? 0,
'tax' => $item['tax'] ?? 0,
'serial_numbers' => !empty($serials) ? json_encode($serials) : null,
'created_at' => now(),
'updated_at' => now(),
];
if ($request->status === 'completed') {
foreach ($request->products as $stockId => $item) {
// Get the actual FROM stock
$fromStock = Stock::where('id', $stockId)
->when($fromBranch, fn($q) => $q->where('branch_id', $fromBranch))
->when($fromWh, fn($q) => $q->where('warehouse_id', $fromWh))
->first();
// FROM stock
$fromStock = Stock::where('id', $stock->id)
->when($fromBranch, fn($q) => $q->where('branch_id', $fromBranch))
->when($fromWh, fn($q) => $q->where('warehouse_id', $fromWh))
->first();
if (!$fromStock) {
return response()->json([
'message' => "Stock not found in source (branch/warehouse) for stock ID: {$stockId}"
], 400);
}
if (!$fromStock || $fromStock->productStock < $quantity) {
return response()->json(['message' => 'Insufficient stock for product ID: ' . $stock->product_id], 400);
}
if ($fromStock->productStock < $item['quantity']) {
return response()->json([
'message' => "Insufficient stock in source for product ID: {$fromStock->product_id}, available: {$fromStock->productStock}"
], 400);
}
// Remove serials from source
if (!empty($serials)) {
$existingSerials = is_array($fromStock->serial_numbers) ? $fromStock->serial_numbers : json_decode($fromStock->serial_numbers ?? '[]', true);
$fromStock->serial_numbers = array_values(array_diff($existingSerials, $serials));
}
// Decrease FROM stock
$fromStock->decrement('productStock', $item['quantity']);
$fromStock->decrement('productStock', $quantity);
$fromStock->save();
// Get the TO stock
$toStock = Stock::where('product_id', $fromStock->product_id)
->when($toBranch, fn($q) => $q->where('branch_id', $toBranch))
->when($toWh, fn($q) => $q->where('warehouse_id', $toWh))
->when(!is_null($fromStock->batch_no), fn($q) => $q->where('batch_no', $fromStock->batch_no))
->first();
// TO stock
$toStock = Stock::where('product_id', $fromStock->product_id)
->when($toBranch, fn($q) => $q->where('branch_id', $toBranch))
->when($toWh, fn($q) => $q->where('warehouse_id', $toWh))
->where(function ($q) use ($fromStock) {
// if destination stock batch is NULL, skip batch compare
$q->whereNull('batch_no')
// otherwise match same batch
->orWhere('batch_no', $fromStock->batch_no);
})
->first();
if (!$toStock) {
$toStock = new Stock([
'business_id' => auth()->user()->business_id,
'product_id' => $fromStock->product_id,
'warehouse_id' => $toWh,
'batch_no' => $fromStock->batch_no,
'productStock' => 0,
'productPurchasePrice' => $fromStock->productPurchasePrice,
'profit_percent' => $fromStock->profit_percent,
'productSalePrice' => $fromStock->productSalePrice,
'productWholeSalePrice' => $fromStock->productWholeSalePrice,
'productDealerPrice' => $fromStock->productDealerPrice,
'mfg_date' => $fromStock->mfg_date,
'expire_date' => $fromStock->expire_date,
]);
if (!$toStock) {
$toStock = new Stock([
'business_id' => $user->business_id,
'product_id' => $fromStock->product_id,
'warehouse_id' => $toWh,
'productStock' => 0,
'productPurchasePrice' => $fromStock->productPurchasePrice,
'profit_percent' => $fromStock->profit_percent,
'productSalePrice' => $fromStock->productSalePrice,
'productWholeSalePrice' => $fromStock->productWholeSalePrice,
'productDealerPrice' => $fromStock->productDealerPrice,
'mfg_date' => $fromStock->mfg_date,
'expire_date' => $fromStock->expire_date,
'serial_numbers' => !empty($serials) ? $serials : null,
]);
// if active branch and to branch is null then use active branch id
if ($toBranch == null && $user->active_branch_id){
$toStock->branch_id = $user->active_branch_id;
}else{
$toStock->branch_id = $toBranch;
}
// Safe branch assignment
$toStock->branch_id = $toBranch ?? $user->active_branch_id;
// Update product_type
if ($fromStock->product->product_type !== 'variant') {
$fromStock->product->update([
'product_type' => 'variant'
]);
}
// Save without events/global scopes
Stock::withoutEvents(function () use ($toStock) {
$toStock->save();
});
}
// Skip booted from model
Stock::withoutEvents(function () use ($toStock) {
$toStock->save();
});
}
// Increment destination stock safely
$toStock->increment('productStock', $quantity);
// Increase TO stock safely
$toStock->increment('productStock', $item['quantity']);
// Merge serial numbers if exist
if (!empty($serials)) {
$toStockSerials = is_array($toStock->serial_numbers)
? $toStock->serial_numbers
: json_decode($toStock->serial_numbers ?? '[]', true);
$toStock->serial_numbers = array_values(array_unique(array_merge($toStockSerials, $serials)));
$toStock->save();
}
}
}
TransferProduct::insert($transferProductData);
TransferProduct::insert($transferProducts);
DB::commit();
return response()->json([
'message' => __('Transfer saved successfully.'),
'data' => $transfer,
'redirect' => route('business.transfers.index'),
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'message' => 'Error: ' . $e->getMessage(),
'success' => false,
'message' => $e->getMessage()
], 500);
}
}
@@ -313,6 +323,19 @@ public function edit($id)
public function update(Request $request, $id)
{
// Normalize serial_numbers
if ($request->has('products')) {
$products = $request->products;
foreach ($products as $key => $item) {
if (!empty($item['serial_numbers']) && is_string($item['serial_numbers'])) {
$products[$key]['serial_numbers'] = json_decode($item['serial_numbers'], true);
}
}
$request->merge(['products' => $products]);
}
$request->validate([
'from_branch_id' => 'nullable|exists:branches,id',
'to_branch_id' => 'nullable|exists:branches,id|required_with:from_branch_id',
@@ -327,11 +350,12 @@ public function update(Request $request, $id)
'products.*.unit_price' => 'required|numeric|min:0',
'products.*.discount' => 'nullable|numeric|min:0',
'products.*.tax' => 'nullable|numeric|min:0',
'products.*.serial_numbers' => 'nullable|array',
]);
$transfer = Transfer::findOrFail($id);
if ($request->status == 'cancelled') {
if ($request->status === 'cancelled') {
$transfer->update(['status' => 'cancelled']);
return response()->json([
'message' => __('Transfer cancelled successfully.'),
@@ -345,49 +369,28 @@ public function update(Request $request, $id)
$fromWh = $request->from_warehouse_id;
$toWh = $request->to_warehouse_id;
// Transfer validation
if ($user->active_branch_id && $toBranch && $toWh) {
return response()->json([
'message' => 'You cannot transfer to another branch warehouse.'
], 400);
}
if (!$toBranch && !$toWh) {
// to_branch or to_warehouse no one exist
return response()->json([
'message' => 'Invalid transfer request. Please select a destination branch or warehouse.'
], 400);
}
if ($fromBranch && !$fromWh) {
if ($fromBranch == $toBranch) {
return response()->json(['message' => 'Transfer not allowed: Same branch transfer is not possible.'], 400);
}
} elseif (!$fromBranch && $fromWh) {
if ($fromWh == $toWh) {
return response()->json(['message' => 'Transfer not allowed: Same warehouse transfer is not possible.'], 400);
}
} elseif ($fromBranch && $fromWh) {
if ($fromBranch == $toBranch && $fromWh == $toWh) {
return response()->json(['message' => 'Transfer not allowed: Same warehouse transfer within the same branch.'], 400);
}
} else {
return response()->json(['message' => 'Invalid transfer request. Please provide branch or warehouse information.'], 400);
}
DB::beginTransaction();
try {
$oldStatus = $transfer->status;
$subTotal = $totalDiscount = $totalTax = 0;
// Calculate totals using serial count
foreach ($request->products as $item) {
$subTotal += $item['quantity'] * $item['unit_price'];
$serials = !empty($item['serial_numbers']) ? $item['serial_numbers'] : [];
$qty = !empty($serials) ? count($serials) : $item['quantity'];
$subTotal += $qty * $item['unit_price'];
$totalDiscount += $item['discount'] ?? 0;
$totalTax += $item['tax'] ?? 0;
}
$shipping = $request->shipping_charge ?? 0;
$grandTotal = $subTotal + $totalTax - $totalDiscount + $shipping;
// Update transfer
$transfer->update([
'user_id' => auth()->id(),
'from_branch_id' => $fromBranch,
'to_branch_id' => $toBranch,
'from_warehouse_id' => $fromWh,
@@ -402,25 +405,25 @@ public function update(Request $request, $id)
'grand_total' => $grandTotal,
]);
// Update TransferProduct
// Delete old transfer products
TransferProduct::where('transfer_id', $transfer->id)->delete();
$transferProductData = [];
foreach ($request->products as $stockId => $item) {
$transferProductData[] = [
$serials = !empty($item['serial_numbers']) ? $item['serial_numbers'] : [];
$quantity = !empty($serials) ? count($serials) : $item['quantity'];
// Insert updated transfer product
TransferProduct::create([
'transfer_id' => $transfer->id,
'stock_id' => $stockId,
'quantity' => $item['quantity'] ?? 0,
'quantity' => $quantity,
'unit_price' => $item['unit_price'] ?? 0,
'discount' => $item['discount'] ?? 0,
'tax' => $item['tax'] ?? 0,
'created_at' => now(),
'updated_at' => now(),
];
}
TransferProduct::insert($transferProductData);
'serial_numbers' => !empty($serials) ? $serials : null,
]);
// Stock Handling
foreach ($request->products as $stockId => $item) {
$fromStock = Stock::where('id', $stockId)
->when($fromBranch, fn($q) => $q->where('branch_id', $fromBranch))
->when($fromWh, fn($q) => $q->where('warehouse_id', $fromWh))
@@ -431,18 +434,23 @@ public function update(Request $request, $id)
'message' => "From stock not found for stock ID: {$stockId}"
], 400);
}
$toStock = Stock::where('product_id', $fromStock->product_id)
->when($toBranch, fn($q) => $q->where('branch_id', $toBranch))
->when($toWh, fn($q) => $q->where('warehouse_id', $toWh))
->when(!is_null($fromStock->batch_no), fn($q) => $q->where('batch_no', $fromStock->batch_no))
->where(function ($q) use ($fromStock) {
// if destination stock batch is NULL, skip batch compare
$q->whereNull('batch_no')
// otherwise match same batch
->orWhere('batch_no', $fromStock->batch_no);
})
->first();
if (!$toStock) {
$toStock = new Stock([
'business_id' => auth()->user()->business_id,
'business_id' => $user->business_id,
'product_id' => $fromStock->product_id,
'warehouse_id' => $toWh,
'batch_no' => $fromStock->batch_no,
'productStock' => 0,
'productPurchasePrice' => $fromStock->productPurchasePrice,
'profit_percent' => $fromStock->profit_percent,
@@ -453,31 +461,43 @@ public function update(Request $request, $id)
'expire_date' => $fromStock->expire_date,
]);
// if active branch and to branch is null then use active branch id
if ($toBranch == null && $user->active_branch_id){
$toStock->branch_id = $user->active_branch_id;
}else{
$toStock->branch_id = $toBranch;
}
// Update product_type
if ($fromStock->product->product_type !== 'variant') {
$fromStock->product->update([
'product_type' => 'variant'
]);
}
$toStock->branch_id = $toBranch ?? $user->active_branch_id;
Stock::withoutEvents(fn() => $toStock->save());
}
// Stock movement based on status change
if ($oldStatus !== $request->status) {
if ($oldStatus === 'pending' && $request->status === 'completed') {
if ($fromStock->productStock >= $item['quantity']) {
$fromStock->decrement('productStock', $item['quantity']);
$toStock->increment('productStock', $item['quantity']);
// Only move stock if changing pending to completed
if ($oldStatus === 'pending' && $request->status === 'completed') {
// Handle serials for source
if (!empty($serials)) {
$existing = $fromStock->serial_numbers ?? [];
foreach ($serials as $s) {
if (!in_array($s, $existing)) {
return response()->json([
'message' => "Serial {$s} not found in source stock."
], 400);
}
}
// Remove from source
$fromStock->serial_numbers = array_values(array_diff($existing, $serials));
$fromStock->productStock = count($fromStock->serial_numbers);
if ($fromStock->productStock === 0) {
$fromStock->serial_numbers = null;
}
} else {
$fromStock->decrement('productStock', $quantity);
}
$fromStock->save();
// Handle serials for destination
if (!empty($serials)) {
$toExisting = $toStock->serial_numbers ?? [];
$toStock->serial_numbers = array_values(array_unique(array_merge($toExisting, $serials)));
$toStock->productStock = count($toStock->serial_numbers);
} else {
$toStock->increment('productStock', $quantity);
}
$toStock->save();
}
}
@@ -487,6 +507,7 @@ public function update(Request $request, $id)
'message' => __('Transfer updated successfully.'),
'redirect' => route('business.transfers.index'),
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
@@ -494,8 +515,7 @@ public function update(Request $request, $id)
], 500);
}
}
public function destroy($id)
public function destroy(string $id)
{
$business_id = auth()->user()->business_id;
$transfer = Transfer::with('transferProducts')->where('business_id', $business_id)->findOrFail($id);
@@ -511,7 +531,10 @@ public function destroy($id)
$toStock = Stock::where('product_id', $tp->product_id)
->where('warehouse_id', $transfer->to_warehouse_id)
->where('branch_id', $transfer->to_branch_id)
->where('batch_no', optional($stock)->batch_no)
->where(function ($q) use ($stock) {
$q->whereNull('batch_no') // if batch NULL → skip compare
->orWhere('batch_no', optional($stock)->batch_no);
})
->first();
if (!$toStock || $toStock->quantity < $tp->quantity) {
@@ -528,8 +551,12 @@ public function destroy($id)
$toStock = Stock::where('product_id', $tp->product_id)
->where('warehouse_id', $transfer->to_warehouse_id)
->where('branch_id', $transfer->to_branch_id)
->where('batch_no', optional($stock)->batch_no)
->where(function ($q) use ($stock) {
$q->whereNull('batch_no')
->orWhere('batch_no', optional($stock)->batch_no);
})
->first();
if ($toStock) {
$toStock->decrement('quantity', $tp->quantity);
}
@@ -538,8 +565,12 @@ public function destroy($id)
$fromStock = Stock::where('product_id', $tp->product_id)
->where('warehouse_id', $transfer->from_warehouse_id)
->where('branch_id', $transfer->from_branch_id)
->where('batch_no', optional($stock)->batch_no)
->where(function ($q) use ($stock) {
$q->whereNull('batch_no')
->orWhere('batch_no', optional($stock)->batch_no);
})
->first();
if ($fromStock) {
$fromStock->increment('quantity', $tp->quantity);
}
@@ -583,7 +614,10 @@ public function deleteAll(Request $request)
$toStock = Stock::where('product_id', $tp->product_id)
->where('warehouse_id', $transfer->to_warehouse_id)
->where('branch_id', $transfer->to_branch_id)
->where('batch_no', optional($stock)->batch_no)
->where(function ($q) use ($stock) {
$q->whereNull('batch_no')
->orWhere('batch_no', optional($stock)->batch_no);
})
->first();
if (!$toStock || $toStock->quantity < $tp->quantity) {
@@ -604,8 +638,12 @@ public function deleteAll(Request $request)
$toStock = Stock::where('product_id', $tp->product_id)
->where('warehouse_id', $transfer->to_warehouse_id)
->where('branch_id', $transfer->to_branch_id)
->where('batch_no', optional($stock)->batch_no)
->where(function ($q) use ($stock) {
$q->whereNull('batch_no')
->orWhere('batch_no', optional($stock)->batch_no);
})
->first();
if ($toStock) {
$toStock->decrement('quantity', $tp->quantity);
}
@@ -614,8 +652,12 @@ public function deleteAll(Request $request)
$fromStock = Stock::where('product_id', $tp->product_id)
->where('warehouse_id', $transfer->from_warehouse_id)
->where('branch_id', $transfer->from_branch_id)
->where('batch_no', optional($stock)->batch_no)
->where(function ($q) use ($stock) {
$q->whereNull('batch_no')
->orWhere('batch_no', optional($stock)->batch_no);
})
->first();
if ($fromStock) {
$fromStock->increment('quantity', $tp->quantity);
}
@@ -648,4 +690,49 @@ public function exportCsv()
{
return Excel::download(new ExportTransfer, 'transfer.csv');
}
public function exportPdf(Request $request)
{
$user = auth()->user();
$activeBranchId = $user->active_branch_id;
$transfers = Transfer::with(['fromWarehouse:id,name', 'toWarehouse:id,name', 'toBranch:id,name', 'fromBranch:id,name', 'transferProducts'])
->where('business_id', $user->business_id)
->when($activeBranchId, function ($q) use ($activeBranchId) {
$q->where(function ($query) use ($activeBranchId) {
$query->where('from_branch_id', $activeBranchId)
->orWhere('to_branch_id', $activeBranchId);
});
})
->when($request->branch_id && !$activeBranchId, function ($q) use ($request) {
$q->where(function ($query) use ($request) {
$query->where('from_branch_id', $request->branch_id)->orWhere('to_branch_id', $request->branch_id);
});
})
->when($request->search, function ($q) use ($request) {
$search = $request->search;
$q->where(function ($q) use ($search) {
$q->where('note', 'like', '%' . $search . '%')
->orWhere('status', 'like', '%' . $search . '%')
->orWhere('invoice_no', 'like', '%' . $search . '%')
->orWhereHas('fromWarehouse', function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%');
})
->orWhereHas('toWarehouse', function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%');
})
->orWhereHas('toBranch', function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%');
})
->orWhereHas('fromBranch', function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%');
});
});
})
->latest()
->limit(request('per_page') ?? 20)->get();
return PdfService::render('business::transfers.pdf', compact('transfers'),'transfers.pdf');
}
}

View File

@@ -4,6 +4,7 @@
use App\Http\Controllers\Controller;
use App\Models\Vat;
use App\Models\VatStateItem;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
@@ -19,22 +20,41 @@ public function __construct()
public function index(Request $request)
{
$vats = Vat::where('business_id', auth()->user()->business_id)->orderBy('status', 'desc')->whereNull('sub_vat')->latest()->paginate(20);
$vat_groups = Vat::where('business_id', auth()->user()->business_id)->orderBy('status', 'desc')->whereNotNull('sub_vat')->latest()->paginate(20);
$business_id = auth()->user()->business_id;
$vats = Vat::where('business_id', $business_id)
->orderBy('status', 'desc')
->whereNull('sub_vat')
->where('manage_state', 0)
->latest()
->paginate(20);
$vat_groups = Vat::where('business_id', $business_id)
->where(function ($query) {
$query->whereNotNull('sub_vat')
->orWhere('manage_state', 1);
})
->with('stateVats.childVat:id,name,rate')
->orderBy('status', 'desc')
->latest()
->paginate(20);
return view('business::vats.index', compact('vats', 'vat_groups'));
}
public function acnooFilter(Request $request)
{
$vats = Vat::where('business_id', auth()->user()->business_id)->whereNull('sub_vat')
->when($request->search, function ($query) use ($request) {
$query->where(function ($q) use ($request) {
$search = $request->search;
$q->where('name', 'like', "%$search%");
});
})
->latest()
->paginate(20);
$vats = Vat::where('business_id', auth()->user()->business_id)
->whereNull('sub_vat')
->when($request->search, function ($query) use ($request) {
$query->where(function ($q) use ($request) {
$search = $request->search;
$q->where('name', 'like', "%$search%");
});
})
->where('manage_state', 0)
->latest()
->paginate(20);
if ($request->ajax()) {
return response()->json([
@@ -47,75 +67,135 @@ public function acnooFilter(Request $request)
public function VatGroupFilter(Request $request)
{
$vat_groups = Vat::where('business_id', auth()->user()->business_id)->whereNotNull('sub_vat')
->when($request->search, function ($query) use ($request) {
$query->where(function ($q) use ($request) {
$search = $request->search;
$q->where('name', 'like', "%$search%");
});
})
->latest()
->paginate(20);
$vat_groups = Vat::where('business_id', auth()->user()->business_id)
->when($request->search, function ($query) use ($request) {
$query->where(function ($q) use ($request) {
$search = $request->search;
$q->where('name', 'like', "%$search%");
});
})
->with('stateVats.childVat:id,name')
->where(function ($query) {
$query->whereNotNull('sub_vat')
->orWhere('manage_state', 1);
})
->latest()
->paginate(20);
if ($request->ajax()) {
return response()->json([
'data' => view('business::vat-groups.datas', compact('vat_groups'))->render()
]);
}
return redirect(url()->previous());
}
// Vat Group Create
public function create()
{
$vats = Vat::where('business_id', auth()->user()->business_id)->where('status','1')->whereNull('sub_vat')->latest()->get();
$vats = Vat::where('business_id', auth()->user()->business_id)
->where('status','1')
->whereNull('sub_vat')
->where('manage_state', 0)
->latest()
->get();
return view('business::vat-groups.create',compact('vats'));
}
public function store(Request $request)
{
$request->validate([
'vat_ids' => 'array',
'rate' => 'nullable|numeric',
'name' => 'required|string|max:255',
'vat_ids' => 'required_if:rate,null',
'rate' => 'required_if:rate,null|numeric',
'manage_state' => 'nullable|integer|in:0,1',
]);
// single vat
if ($request->rate && !$request->vat_ids) {
Vat::create($request->all() + [
'business_id' => auth()->user()->business_id,
]);
$businessId = auth()->user()->business_id;
}
// group vat
elseif (!$request->rate && $request->vat_ids) {
// SINGLE VAT
if ($request->filled('rate') && !$request->vat_ids) {
$vats = Vat::whereIn('id', $request->vat_ids)->select('id', 'name', 'rate')->get();
$vat = Vat::create([
'name' => $request->name,
'rate' => $request->rate,
'business_id' => $businessId,
'manage_state' => 0,
]);
} elseif ($request->vat_ids && !$request->manage_state) { // GROUP VAT (OLD SYSTEM)
$vats = Vat::whereIn('id', $request->vat_ids)
->where('business_id', $businessId)
->select('id', 'name', 'rate')
->get();
$tax_rate = 0;
$sub_vats = [];
foreach ($vats as $vat) {
foreach ($vats as $vatItem) {
$sub_vats[] = [
'id' => $vat->id,
'name' => $vat->name,
'rate' => $vat->rate,
'id' => $vatItem->id,
'name' => $vatItem->name,
'rate' => $vatItem->rate,
];
$tax_rate += $vat->rate;
$tax_rate += $vatItem->rate;
}
Vat::create([
'rate' => $tax_rate,
'sub_vat' => $sub_vats,
'name' => $request->name,
'business_id' => auth()->user()->business_id,
$vat = Vat::create([
'rate' => $tax_rate,
'sub_vat' => $sub_vats,
'name' => $request->name,
'business_id' => $businessId,
'manage_state' => 0,
]);
} elseif ($request->manage_state == 1) { // STATE WISE VAT (NEW PIVOT SYSTEM)
$request->validate([
'inner_vat_ids' => 'required|array',
'outer_vat_ids' => 'required|array',
]);
} else {
$innerIds = $request->inner_vat_ids ?? [];
$outerIds = $request->outer_vat_ids ?? [];
$innerRate = Vat::whereIn('id', $innerIds)
->where('business_id', $businessId)
->sum('rate');
$outerRate = Vat::whereIn('id', $outerIds)
->where('business_id', $businessId)
->sum('rate');
$vat = Vat::create([
'name' => $request->name,
'rate' => $innerRate ?: $outerRate,
'business_id' => $businessId,
'manage_state' => 1,
]);
// Save pivot inner
foreach ($innerIds as $id) {
VatStateItem::create([
'parent_vat_id' => $vat->id,
'child_vat_id' => $id,
'type' => 'inner'
]);
}
// Save pivot outer
foreach ($outerIds as $id) {
VatStateItem::create([
'parent_vat_id' => $vat->id,
'child_vat_id' => $id,
'type' => 'outer'
]);
}
}
else {
return response()->json([
'message' => 'Invalid data format.',
'message' => 'Invalid data format.'
], 406);
}
@@ -128,27 +208,43 @@ public function store(Request $request)
// Vat Group Edit
public function edit($id)
{
$vat = Vat::where('business_id', auth()->user()->business_id)->findOrFail($id);
$vats = Vat::where('business_id', auth()->user()->business_id)->where('status','1')->whereNull('sub_vat')->latest()->paginate(20);
$vat = Vat::with(['innerStateVats', 'outerStateVats'])->where('business_id', auth()->user()->business_id)->findOrFail($id);
$vats = Vat::where('business_id', auth()->user()->business_id)
->where('status','1')
->whereNull('sub_vat')
->where('manage_state', 0)
->latest()
->get();
return view('business::vat-groups.edit', compact('vat', 'vats'));
}
public function update(Request $request, Vat $vat)
{
$request->validate([
'vat_ids' => 'array',
'rate' => 'nullable|numeric',
'name' => 'required|string|max:255',
'vat_ids' => 'required_if:rate,null',
'rate' => 'required_if:rate,null|numeric',
'manage_state' => 'nullable|integer|in:0,1',
]);
DB::beginTransaction();
try {
// Single VAT update
if ($request->rate && !$request->vat_ids) {
$businessId = auth()->user()->business_id;
$vat->update($request->only(['name', 'rate', 'status']));
VatStateItem::where('parent_vat_id', $vat->id)->delete();
// SINGLE VAT
if ($request->filled('rate') && !$request->vat_ids && !$request->manage_state) {
$vat->update([
'name' => $request->name,
'rate' => $request->rate,
'status' => $request->status ?? $vat->status,
'manage_state' => 0,
]);
// Update group VATs containing this single VAT
$vatGroupExist = Vat::where('sub_vat', 'LIKE', '%"id":' . $vat->id . '%')->get();
foreach ($vatGroupExist as $group) {
$subVats = collect($group->sub_vat)->map(function ($subVat) use ($vat) {
@@ -158,7 +254,6 @@ public function update(Request $request, Vat $vat)
}
return $subVat;
});
$group->update([
'rate' => $subVats->sum('rate'),
'sub_vat' => $subVats->toArray(),
@@ -166,30 +261,79 @@ public function update(Request $request, Vat $vat)
}
}
// Group VAT update
elseif (!$request->rate && $request->vat_ids) {
// GROUP VAT (OLD SYSTEM)
elseif ($request->vat_ids && !$request->manage_state) {
$vats = Vat::whereIn('id', $request->vat_ids)->select('id', 'name', 'rate')->get();
$vats = Vat::whereIn('id', $request->vat_ids)
->where('business_id', $businessId)
->select('id', 'name', 'rate')
->get();
$tax_rate = 0;
$sub_vats = [];
foreach ($vats as $single_tax) {
foreach ($vats as $vatItem) {
$sub_vats[] = [
'id' => $single_tax->id,
'name' => $single_tax->name,
'rate' => $single_tax->rate,
'id' => $vatItem->id,
'name' => $vatItem->name,
'rate' => $vatItem->rate,
];
$tax_rate += $single_tax->rate;
$tax_rate += $vatItem->rate;
}
$vat->update([
'name' => $request->name,
'rate' => $tax_rate,
'sub_vat' => $sub_vats,
'name' => $request->name,
'status' => $request->status ?? $vat->status,
'manage_state' => 0,
]);
} else {
}
// STATE-WISE VAT (NEW SYSTEM)
elseif ($request->manage_state == 1) {
$innerIds = $request->inner_vat_ids ?? [];
$outerIds = $request->outer_vat_ids ?? [];
$innerRate = Vat::whereIn('id', $innerIds)
->where('business_id', $businessId)
->sum('rate');
$outerRate = Vat::whereIn('id', $outerIds)
->where('business_id', $businessId)
->sum('rate');
$vat->update([
'name' => $request->name,
'rate' => $innerRate ?: $outerRate,
'status' => $request->status ?? $vat->status,
'manage_state' => 1,
'sub_vat' => null,
]);
// Delete old pivot entries
VatStateItem::where('parent_vat_id', $vat->id)->delete();
// Insert inner VAT pivot
foreach ($innerIds as $id) {
VatStateItem::create([
'parent_vat_id' => $vat->id,
'child_vat_id' => $id,
'type' => 'inner',
]);
}
// Insert outer VAT pivot
foreach ($outerIds as $id) {
VatStateItem::create([
'parent_vat_id' => $vat->id,
'child_vat_id' => $id,
'type' => 'outer',
]);
}
}
else {
DB::rollBack();
return response()->json([
'message' => 'Invalid data format.',
@@ -197,14 +341,17 @@ public function update(Request $request, Vat $vat)
}
DB::commit();
return response()->json([
'message' => 'Vat updated successfully',
'redirect' => route('business.vats.index'),
]);
} catch (\Exception $e) {
DB::rollback();
return response()->json(['message' => __('Somethings went wrong!')], 404);
return response()->json([
'message' => __('Somethings went wrong!'),
'error' => $e->getMessage(),
], 500);
}
}

View File

@@ -17,6 +17,7 @@
use App\Models\PurchaseReturnDetail;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
class DashboardController extends Controller
{
@@ -196,17 +197,17 @@ public function revenue()
$data['loss'] = Sale::where('business_id', auth()->user()->business_id)
->whereYear('created_at', request('year') ?? date('Y'))
->where('lossProfit', '<', 0)
->selectRaw('MONTHNAME(created_at) as month, SUM(ABS(lossProfit)) as total')
->orderBy('created_at')
->groupBy('created_at')
->selectRaw("TO_CHAR(created_at, 'FMMonth') as month, SUM(ABS(\"lossProfit\")) as total")
->groupBy(DB::raw("TO_CHAR(created_at, 'FMMonth')"))
->orderBy(DB::raw("MIN(created_at)"))
->get();
$data['profit'] = Sale::where('business_id', auth()->user()->business_id)
->whereYear('created_at', request('year') ?? date('Y'))
->where('lossProfit', '>=', 0)
->selectRaw('MONTHNAME(created_at) as month, SUM(ABS(lossProfit)) as total')
->orderBy('created_at')
->groupBy('created_at')
->selectRaw("TO_CHAR(created_at, 'FMMonth') as month, SUM(ABS(\"lossProfit\")) as total")
->groupBy(DB::raw("TO_CHAR(created_at, 'FMMonth')"))
->orderBy(DB::raw("MIN(created_at)"))
->get();
return response()->json($data);

View File

@@ -3,16 +3,16 @@
namespace Modules\Business\App\Http\Controllers;
use App\Events\MultiPaymentProcessed;
use App\Http\Controllers\Controller;
use App\Models\Branch;
use App\Models\Party;
use App\Models\PaymentType;
use App\Models\Stock;
use App\Models\Branch;
use App\Models\Purchase;
use Illuminate\Http\Request;
use App\Models\PurchaseReturn;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use App\Models\PurchaseReturnDetail;
use App\Services\PurchaseVatTransaction;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class PurchaseReturnController extends Controller
{
@@ -66,7 +66,6 @@ public function index(Request $request)
return view('business::purchase-returns.index', compact('purchases', 'branches'));
}
public function create(Request $request)
{
$business_id = auth()->user()->business_id;
@@ -80,56 +79,90 @@ public function create(Request $request)
$discount_per_unit_factor = $total_purchase_price > 0 ? $purchase->discountAmount / $total_purchase_price : 0;
$payment_types = PaymentType::where('business_id', $business_id)->whereStatus(1)->latest()->get();
return view('business::purchase-returns.create', compact('purchase', 'discount_per_unit_factor','payment_types'));
}
public function store(Request $request)
{
// split-array structure from front-end request
$products = [];
for ($index = 0; $index < count($request->products); $index += 3) {
$products[] = [
'detail_id' => $request->products[$index]['detail_id'] ?? null,
'return_qty' => $request->products[$index + 1]['return_qty'] ?? 0,
'serial_numbers' => $request->products[$index + 2]['serial_numbers'] ?? '[]',
];
}
$request->merge(['products' => $products]);
$request->validate([
'purchase_id' => 'required|exists:purchases,id',
'return_qty' => 'required|array',
'products' => 'required|array|min:1',
'products.*.detail_id' => 'required|exists:purchase_details,id',
'products.*.return_qty' => 'required|integer',
'products.*.serial_numbers' => 'nullable|string',
'payments' => 'nullable|array',
]);
DB::beginTransaction();
try {
$purchase = Purchase::with('details')
->where('business_id', auth()->user()->business_id)
$business_id = auth()->user()->business_id;
$purchase = Purchase::with('details.stock')
->where('business_id', $business_id)
->findOrFail($request->purchase_id);
// Calculate total discount factor
$total_discount = $purchase->discountAmount;
$total_purchase_amount = $purchase->details->sum(fn($detail) => $detail->productPurchasePrice * $detail->quantities);
$total_purchase_amount = $purchase->details->sum(fn($d) => $d->productPurchasePrice * $d->quantities);
$discount_per_unit_factor = $total_purchase_amount > 0 ? $total_discount / $total_purchase_amount : 0;
$purchase_return = PurchaseReturn::create([
'business_id' => auth()->user()->business_id,
'purchase_id' => $request->purchase_id,
'business_id' => $business_id,
'purchase_id' => $purchase->id,
'invoice_no' => $purchase->invoiceNumber,
'return_date' => now(),
]);
$purchase_return_detail_data = [];
$total_return_amount = 0;
$total_return_discount = 0;
// Loop through each purchase detail and process the return
foreach ($purchase->details as $key => $detail) {
$requested_qty = $request->return_qty[$key];
foreach ($request->products as $item) {
if ($requested_qty <= 0) {
continue;
}
$detail = $purchase->details->where('id', $item['detail_id'])->first();
if (!$detail) continue;
$requested_qty = (int) $item['return_qty'];
if ($requested_qty <= 0) continue;
// Check if return quantity exceeds the purchased quantity
if ($requested_qty > $detail->quantities) {
return response()->json([
'message' => "You can't return more than the ordered quantity of {$detail->quantities}.",
'message' => "You can't return more than purchased quantity ({$detail->quantities})."
], 400);
}
// Calculate per-unit discount and return amounts
$serials = !empty($item['serial_numbers']) ? json_decode($item['serial_numbers'], true) : [];
// Serial number validation and stock update
if (!empty($serials)) {
if (count($serials) !== $requested_qty) {
return response()->json(['message' => 'Serial count must match return quantity.'], 400);
}
$stockSerials = $detail->stock->serial_numbers ?? [];
$invalid = array_diff($serials, $stockSerials);
if (!empty($invalid)) {
return response()->json([
'message' => 'Invalid serial numbers detected.',
'invalid_serials' => array_values($invalid),
], 400);
}
// Remove serials from stock
$stock_remaining = array_values(array_diff($stockSerials, $serials));
$detail->stock->update(['serial_numbers' => $stock_remaining]);
}
//Return amount calculation
$unit_discount = $detail->productPurchasePrice * $discount_per_unit_factor;
$return_discount = $unit_discount * $requested_qty;
$return_amount = ($detail->productPurchasePrice - $unit_discount) * $requested_qty;
@@ -137,100 +170,88 @@ public function store(Request $request)
$total_return_amount += $return_amount;
$total_return_discount += $return_discount;
// Update stock & purchase details
Stock::where('id', $detail->stock_id)->decrement('productStock', $requested_qty);
//Update stock & purchase detail
$detail->stock->decrement('productStock', $requested_qty);
$detail->quantities -= $requested_qty;
$detail->timestamps = false;
$detail->save();
// Collect return detail data
$purchase_return_detail_data[] = [
'purchase_detail_id' => $detail->id,
'purchase_return_id' => $purchase_return->id,
'return_qty' => $requested_qty,
'business_id' => auth()->user()->business_id,
'return_amount' => $return_amount,
];
}
// Remove serials from purchase detail
if (!empty($serials) && !empty($detail->serial_numbers)) {
$remainingDetailSerials = array_values(array_diff($detail->serial_numbers ?? [], $serials));
$detail->update(['serial_numbers' => $remainingDetailSerials]);
}
// Insert purchase return details
if (!empty($purchase_return_detail_data)) {
PurchaseReturnDetail::insert($purchase_return_detail_data);
$returnDetail = PurchaseReturnDetail::create([
'business_id' => $business_id,
'purchase_return_id' => $purchase_return->id,
'purchase_detail_id' => $detail->id,
'return_qty' => $requested_qty,
'return_amount' => $return_amount,
'serial_numbers' => $serials ?? null,
]);
$previous_returned_qty = PurchaseReturnDetail::where('purchase_detail_id', $detail->id)->sum('return_qty');
$totalQty = $detail->quantities + $previous_returned_qty;
PurchaseVatTransaction::productReturnVatCalculation($detail, $requested_qty, $returnDetail, $totalQty);
}
if ($total_return_amount <= 0) {
return response()->json("You cannot return an empty product.", 400);
return response()->json('You cannot return empty product.', 400);
}
// Update party dues (if applicable)
// Adjust party balances
$party = Party::find($purchase->party_id);
if ($party) {
$refund_amount = $total_return_amount;
$refund = $total_return_amount;
// If party has due, reduce it first
if ($party->due > 0) {
if ($party->due >= $refund_amount) {
$party->decrement('due', $refund_amount);
$refund_amount = 0;
} else {
$refund_amount -= $party->due;
$party->update(['due' => 0]);
}
$deduct = min($party->due, $refund);
$party->decrement('due', $deduct);
$refund -= $deduct;
}
// Any remaining amount should be deducted from wallet
if ($refund_amount > 0 && $party->wallet > 0) {
$deduct = min($party->wallet, $refund_amount);
if ($refund > 0 && $party->wallet > 0) {
$deduct = min($party->wallet, $refund);
$party->decrement('wallet', $deduct);
}
}
// Calculate remaining return amount
$remaining_return_amount = max(0, $total_return_amount - $purchase->dueAmount);
$new_total_amount = max(0, $purchase->totalAmount - $total_return_amount);
// Update purchase record
$purchase->update([
'change_amount' => 0,
'dueAmount' => max(0, $purchase->dueAmount - $total_return_amount),
'paidAmount' => max(0, $purchase->paidAmount - min($purchase->paidAmount, $total_return_amount)),
'totalAmount' => $new_total_amount,
'totalAmount' => max(0, $purchase->totalAmount - $total_return_amount),
'discountAmount' => max(0, $purchase->discountAmount - $total_return_discount),
'isPaid' => $remaining_return_amount > 0 ? 1 : $purchase->isPaid,
]);
$payments = $request->payments;
if (isset($payments['main'])) {
$mainPayment = $payments['main'];
$mainPayment['amount'] = $request->receive_amount ?? 0;
$payments = [$mainPayment];
}
$payments = collect($payments)->map(function ($payment) use ($total_return_amount) {
$payment['amount'] = $total_return_amount;
return $payment;
})->toArray();
// Payment processing
$payments = $request->payments ?? [];
$payments = collect($payments)->map(fn($p) => array_merge($p, ['amount' => $total_return_amount]))->toArray();
event(new MultiPaymentProcessed(
$payments,
$purchase_return->id,
'purchase_return',
$total_return_amount ?? 0,
$total_return_amount
));
$tax_invoice = moduleCheck('TaxInvoiceAddon') && invoice_setting($purchase->business_id) == 'standard_a4';
$invoice_route = $tax_invoice ? route('business.purchases.tax.invoice', $purchase->id) : route('business.purchases.invoice', $purchase->id);
DB::commit();
return response()->json([
'message' => __('Purchase returned successfully.'),
'redirect' => route('business.purchase-returns.index'),
'secondary_redirect_url' => route('business.purchases.invoice', $purchase->id),
'secondary_redirect_url' => $invoice_route,
]);
} catch (\Exception $e) {
DB::rollback();
return response()->json(['message' => __('Something went wrong!')], 500);
return response()->json(['message' => __('Something went wrong!') . ' ' . $e->getMessage()], 500);
}
}
}

View File

@@ -116,6 +116,7 @@ public function store(Request $request)
$sale_return_detail_data = [];
$total_return_amount = 0;
$total_return_discount = 0;
$total_return_vat = 0;
$total_loss_profit_adjustment = 0;
foreach ($sale->details as $key => $detail) {
@@ -138,11 +139,16 @@ public function store(Request $request)
$item_cart_discount = $detail->discount ?? 0;
$total_discount_per_unit = $unit_discount + $item_cart_discount;
// Calculate VAT for the item
$item_vat_percent = $detail->product->vat->rate ?? 0;
$item_vat_amount = ($detail->price * $item_vat_percent) / 100;
$return_discount = $total_discount_per_unit * $requested_qty;
$return_amount = ($detail->price - $total_discount_per_unit + $rounding_amount_per_unit) * $requested_qty;
$return_amount = ($detail->price - $total_discount_per_unit + $rounding_amount_per_unit + $item_vat_amount) * $requested_qty;
$total_return_amount += $return_amount;
$total_return_discount += $return_discount;
$total_return_vat += ($item_vat_amount * $requested_qty);
if ($product && $product->product_type === 'combo') {
@@ -188,17 +194,19 @@ public function store(Request $request)
$detail->timestamps = false;
$detail->save();
$sale_return_detail_data[] = [
$returnDetail = SaleReturnDetails::create([
'business_id' => $business_id,
'sale_detail_id' => $detail->id,
'sale_return_id' => $sale_return->id,
'return_qty' => $requested_qty,
'return_amount' => $return_amount,
];
}
]);
if (!empty($sale_return_detail_data)) {
SaleReturnDetails::insert($sale_return_detail_data);
// Record VAT transaction for the return
if (class_exists('App\Services\VatTransactionService')) {
$totalQty = $detail->quantities + $requested_qty; // Original quantity before this return decrement
\App\Services\VatTransactionService::productReturnVatCalculation($detail, $requested_qty, $returnDetail, $totalQty);
}
}
if ($total_return_amount <= 0) {
@@ -240,6 +248,7 @@ public function store(Request $request)
'paidAmount' => $new_paid,
'totalAmount' => $new_total_amount,
'actual_total_amount' => $new_total_amount,
'vat_amount' => max(0, $sale->vat_amount - $total_return_vat),
'discountAmount' => max(0, $sale->discountAmount - $total_return_discount),
'lossProfit' => $sale->lossProfit - $total_loss_profit_adjustment,
]);

View File

@@ -2,14 +2,16 @@
namespace Modules\Business\App\Http\Controllers;
use App\Models\Option;
use App\Models\Business;
use App\Helpers\HasUploader;
use Illuminate\Http\Request;
use App\Models\BusinessCategory;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use App\Models\Business;
use App\Models\BusinessCategory;
// use App\Models\Country;
use App\Models\Option;
// use App\Models\State;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
class SettingController extends Controller
{
@@ -18,14 +20,16 @@ class SettingController extends Controller
public function index()
{
$setting = Option::where('key', 'business-settings')
->where('value', 'LIKE', '%"business_id":%' . auth()->user()->business_id . '%')
->where('value', 'LIKE', '%"business_id":' . auth()->user()->business_id . '%')
->get()
->firstWhere('value.business_id', auth()->user()->business_id);
$business_categories = BusinessCategory::whereStatus(1)->latest()->get();
$business = Business::findOrFail(auth()->user()->business_id);
$countries = [];
$states = [];
return view('business::settings.general', compact('setting', 'business_categories', 'business'));
return view('business::settings.general', compact('setting', 'business_categories', 'business', 'countries', 'states'));
}
public function update(Request $request, $id)
@@ -56,6 +60,8 @@ public function update(Request $request, $id)
'show_invoice_scanner_logo' => 'nullable|boolean',
'show_a4_invoice_logo' => 'nullable|boolean',
'show_thermal_invoice_logo' => 'nullable|boolean',
// 'country_id' => 'nullable|exists:countries,id',
// 'state_id' => 'nullable|exists:states,id',
]);
DB::beginTransaction();
@@ -71,14 +77,12 @@ public function update(Request $request, $id)
'email' => $request->email,
'vat_name' => $request->vat_name,
'vat_no' => $request->vat_no,
// 'country_id' => $request->country_id,
// 'state_id' => $request->state_id,
]);
$moduleKeys = [
'show_company_name',
'show_phone_number',
'show_address',
'show_email',
'show_vat',
'show_company_name', 'show_phone_number', 'show_address', 'show_email', 'show_vat',
];
$modules = [];
@@ -96,7 +100,7 @@ public function update(Request $request, $id)
if ($setting) {
$setting->update($request->except($data) + [
'value' => $request->except('_token', '_method', 'a4_invoice_logo', 'thermal_invoice_logo', 'invoice_scanner_logo', 'address', 'companyName', 'business_category_id', 'phoneNumber', 'email', 'show_company_name', 'show_phone_number', 'show_address', 'show_email', 'show_vat') + [
'value' => $request->except('_token', '_method', 'a4_invoice_logo', 'thermal_invoice_logo', 'invoice_scanner_logo', 'address', 'companyName', 'business_category_id', 'phoneNumber', 'email', 'show_company_name', 'show_phone_number', 'show_address', 'show_email', 'show_vat' ) + [
'business_id' => $business->id,
'a4_invoice_logo' => $request->a4_invoice_logo ? $this->upload($request, 'a4_invoice_logo', $setting->value['a4_invoice_logo'] ?? null) : ($setting->value['a4_invoice_logo'] ?? null),
'thermal_invoice_logo' => $request->thermal_invoice_logo ? $this->upload($request, 'thermal_invoice_logo', $setting->value['thermal_invoice_logo'] ?? null) : ($setting->value['thermal_invoice_logo'] ?? null),
@@ -140,7 +144,7 @@ public function update(Request $request, $id)
'show_invoice_scanner_logo' => 1,
'show_a4_invoice_logo' => 1,
'show_thermal_invoice_logo' => 1,
'show_warranty' => 1,
'show_warranty' => 1 ,
]),
'created_at' => now(),
'updated_at' => now(),

View File

View File

View File

@@ -1,7 +1,7 @@
{
"name": "Business",
"alias": "business",
"version": "3.1.1",
"version": "3.3",
"description": "",
"keywords": [],
"priority": 0,

View File

@@ -0,0 +1,30 @@
<table class="table" id="datatable">
<thead>
<tr>
<th>{{ __('SL') }}.</th>
<th>{{ __('Date') }}</th>
<th>{{ __('Type') }}</th>
<th>{{ __('Payment') }}</th>
<th>{{ __('Name') }}</th>
<th>{{ __('Amount') }}</th>
</tr>
</thead>
<tbody>
@foreach ($cashes as $cash)
<tr>
<td>{{ $loop->iteration }}</td>
<td>{{ formatted_date($cash->date) }}</td>
<td>{{ ucwords(str_replace('_', ' ', $cash->platform)) }}</td>
<td>{{ ucwords(str_replace('_', ' ', $cash->transaction_type)) }}</td>
<td>{{ $cash->user->name ?? '' }}</td>
<td>
@if ($cash->type == 'credit' || $cash->transaction_type == 'bank_to_cash')
{{ currency_format($cash->amount, currency: business_currency()) }}
@elseif ($cash->type == 'debit' || $cash->transaction_type == 'cash_to_bank')
{{ currency_format(-$cash->amount, currency: business_currency()) }}
@endif
</td>
</tr>
@endforeach
</tbody>
</table>

View File

@@ -0,0 +1,76 @@
@extends('layouts.business.pdf.pdf_layout')
@section('pdf_title')
<div class="table-header justify-content-center border-0 d-print-block text-center">
@include('business::print.header')
<h4 style="text-align: center; margin: 0; padding: 0; font-size: 16px;">
{{ __('Cash Balance List') }}
</h4>
@if ($filter_from_date && $filter_to_date)
<p style="text-align: center; margin: 0; padding: 0; font-weight: 400; font-size: 14px;" class="">{{ __('Duration:') }}
@if ($duration === 'today')
{{ Carbon\Carbon::parse($filter_from_date)->format('d-m-Y') }}
@elseif ($duration === 'yesterday')
{{ Carbon\Carbon::parse($filter_from_date)->format('d-m-Y') }}
@else
{{ Carbon\Carbon::parse($filter_from_date)->format('d-m-Y') }}
{{ __('to') }}
{{ Carbon\Carbon::parse($filter_to_date)->format('d-m-Y') }}
@endif
</p>
@endif
</div>
@endsection
@section('pdf_content')
<table width="100%" cellpadding="6" cellspacing="0"
style="border-collapse: collapse; border:1px solid gainsboro; font-size:12px;">
<thead>
<tr style="background-color: #C52127; color:white;">
<th style="font-size:12px; border:1px solid gainsboro; color: white">
{{ __('Date') }}
</th>
<th style="font-size:12px; border:1px solid gainsboro; color: white">
{{ __('Type') }}
</th>
<th style="font-size:12px; border:1px solid gainsboro; color: white">
{{ __('Payment') }}
</th>
<th style="font-size:12px; border:1px solid gainsboro; color: white">
{{ __('Name') }}
</th>
<th style="font-size:12px; border:1px solid gainsboro; color: white">
{{ __('Amount') }}
</th>
</tr>
</thead>
<tbody>
@foreach($cashes as $cash)
<tr style="background-color: {{ $loop->even ? '#F5F5F5' : '#FFFFFF' }};">
<td style="border:1px solid gainsboro; text-align:center;">
{{ formatted_date($cash->date) }}
</td>
<td style="border:1px solid gainsboro; text-align:center;">
{{ ucwords(str_replace('_', ' ', $cash->platform)) }}
</td>
<td style="border:1px solid gainsboro; text-align:center;">
{{ ucwords(str_replace('_', ' ', $cash->transaction_type)) }}
</td>
<td style="border:1px solid gainsboro; text-align:center;">
{{ $cash->user->name ?? '' }}
</td>
<td style="border:1px solid gainsboro; text-align:center;">
@if ($cash->type == 'credit' || $cash->transaction_type == 'bank_to_cash')
{{ currency_format($cash->amount, currency: business_currency()) }}
@elseif ($cash->type == 'debit' || $cash->transaction_type == 'cash_to_bank')
{{ currency_format(-$cash->amount, currency: business_currency()) }}
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
@endsection

View File

@@ -0,0 +1,67 @@
<div class="modal fade" id="shareModalDues" tabindex="-1" aria-labelledby="shareModalDuesLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="shareModalDuesLabel">{{ __('Share Invoice') }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
@if(empty($copy) && empty($facebook) && empty($twitter) && empty($whatsapp))
<div class="text-center py-4">
<div class="mb-3">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="#dc3545" stroke-width="2"/>
<path d="M12 8V12M12 16H12.01" stroke="#dc3545" stroke-width="2" stroke-linecap="round"/>
</svg>
</div>
<p class="text-muted mb-1" style="font-size: 14px; color: #6c757d;">Unable to share invoice</p>
<p class="m-0" style="font-size: 13px; color: #6c757d;">There is no unique ID or customer phone number available. You cannot share the invoice without a customer phone or a unique ID.</p>
</div>
@else
<input type="text" id="copyUrlInput" value="{{ $copy }}" readonly style="position:absolute; left:-9999px;">
<div class="share-options d-flex justify-content-center gap-4 flex-wrap">
@if($copy)
<a href="javascript:void(0)" class="share-option text-center" id="copyLinkBtn">
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="72" height="72" rx="36" fill="#EBEBEB" />
<path d="M48 31.5H34.5C32.8431 31.5 31.5 32.8431 31.5 34.5V48C31.5 49.6569 32.8431 51 34.5 51H48C49.6569 51 51 49.6569 51 48V34.5C51 32.8431 49.6569 31.5 48 31.5Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
<path d="M25.5 40.5H24C23.2044 40.5 22.4413 40.1839 21.8787 39.6213C21.3161 39.0587 21 38.2956 21 37.5V24C21 23.2044 21.3161 22.4413 21.8787 21.8787C22.4413 21.3161 23.2044 21 24 21H37.5C38.2956 21 39.0587 21.3161 39.6213 21.8787C40.1839 22.4413 40.5 23.2044 40.5 24V25.5" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
<span class="d-block mt-2">Copy Link</span>
</a>
@endif
@if($facebook)
<a href="{{ $facebook }}" target="_blank" class="share-option text-center">
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="72" height="72" rx="36" fill="#EBEBEB" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.2727 33.5C25.8061 33.5 25.5 33.7878 25.5 35.1666V37.6667C25.5 39.0456 25.8061 39.3333 27.2727 39.3333H30.8182V49.3334C30.8182 50.7122 31.1243 51 32.5909 51H36.1364C37.6031 51 37.9091 50.7122 37.9091 49.3334V39.3333H41.8901C43.0025 39.3333 43.2891 39.1301 43.5947 38.1246L44.3544 35.6246C44.8778 33.9021 44.5553 33.5 42.6498 33.5H37.9091V29.3333C37.9091 28.4129 38.7027 27.6667 39.6818 27.6667H44.7273C46.1938 27.6667 46.5 27.3789 46.5 26V22.6667C46.5 21.2878 46.1938 21 44.7273 21H39.6818C34.7865 21 30.8182 24.731 30.8182 29.3333V33.5H27.2727Z" fill="#0F4AFF" />
</svg>
<span class="d-block mt-2">Facebook</span>
</a>
@endif
@if($twitter)
<a href="{{ $twitter }}" target="_blank" class="share-option text-center">
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="72" height="72" rx="36" fill="#EBEBEB" />
<path d="M22.5 49.5L33.8226 38.1774L22.5 49.5ZM49.5 22.5L38.1774 33.8226L49.5 22.5ZM38.1774 33.8226L30 22.5H22.5L33.8226 38.1774M38.1774 33.8226L49.5 49.5H42L33.8226 38.1774" fill="#121535" />
<path d="M22.5 49.5L33.8226 38.1774M38.1774 33.8226L49.5 49.5H42L33.8226 38.1774L22.5 22.5H30L38.1774 33.8226ZM49.5 22.5L38.1774 33.8226" stroke="#121535" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
<span class="d-block mt-2">X</span>
</a>
@endif
@if($whatsapp)
<a href="{{ $whatsapp }}" target="_blank" class="share-option text-center">
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="72" height="72" rx="36" fill="#EBEBEB" />
<path d="M19.875 36C19.875 27.0944 27.0944 19.875 36 19.875C44.9057 19.875 52.125 27.0944 52.125 36C52.125 44.9056 44.9057 52.125 36 52.125C33.7787 52.125 31.6595 51.6752 29.7308 50.8607C29.2249 50.6469 28.883 50.5029 28.6203 50.4067C28.3523 50.3085 28.2554 50.2947 28.2304 50.293C28.0895 50.2833 27.898 50.3188 26.989 50.5621L21.2908 52.0868C20.9027 52.1906 20.4886 52.0796 20.2045 51.7955C19.9204 51.5114 19.8094 51.0974 19.9133 50.7092L21.4379 45.0111C21.6811 44.102 21.7167 43.9105 21.707 43.7697C21.7053 43.7446 21.6915 43.6477 21.5934 43.3797C21.4971 43.1171 21.3531 42.7752 21.1394 42.2693C20.3248 40.3406 19.875 38.2213 19.875 36Z" fill="#00C933" />
<path d="M31.3204 27.3752L31.4593 27.3753C31.7899 27.3753 32.3466 27.3855 32.8611 27.5999C33.4758 27.8561 33.9823 28.3838 34.1119 29.2109L34.1133 29.2195C34.2739 30.2448 34.3962 31.024 34.4763 31.5752C34.5163 31.8511 34.5474 32.08 34.5678 32.2592C34.5846 32.4064 34.6056 32.609 34.5957 32.7784C34.5526 33.5128 34.2547 34.0969 33.9448 34.5493C33.7474 34.8371 33.4833 35.1499 33.2755 35.3959C33.1939 35.4926 33.121 35.5789 33.0642 35.6494L32.9708 35.7653C32.6142 36.2083 32.436 36.4297 32.4416 36.7027C32.4472 36.9758 32.6225 37.1761 32.9732 37.5767C33.1963 37.8316 33.4299 38.0815 33.6741 38.3257C33.9181 38.5697 34.168 38.8034 34.4229 39.0265C34.8235 39.3772 35.0239 39.5525 35.2969 39.5581C35.5699 39.5638 35.7915 39.3854 36.2343 39.0289L36.3504 38.9354C36.4207 38.8787 36.5071 38.8058 36.6037 38.7242C36.8497 38.5165 37.1626 38.2522 37.4505 38.0549C37.9029 37.7449 38.4868 37.447 39.2212 37.4041C39.3907 37.3942 39.5932 37.4152 39.7405 37.4318C39.9196 37.4522 40.1485 37.4833 40.4244 37.5235C40.9756 37.6036 41.7549 37.7257 42.7801 37.8863L42.7887 37.8877C43.6159 38.0173 44.1436 38.524 44.3997 39.1385C44.6142 39.653 44.6244 40.2098 44.6244 40.5403L44.6245 40.6793C44.6257 41.3989 44.6268 42.0626 44.3287 42.7153C44.112 43.1896 43.744 43.5931 43.3764 43.8869C43.0089 44.1808 42.5341 44.4508 42.0238 44.5577C41.3244 44.7041 40.7922 44.5834 40.2228 44.4542L40.144 44.4364C37.2576 43.7845 34.4272 42.2608 32.0831 39.9166C29.7389 37.5724 28.2151 34.742 27.5634 31.8556L27.5455 31.7769C27.4163 31.2075 27.2955 30.6753 27.442 29.9757C27.5488 29.4654 27.819 28.9908 28.1128 28.6232C28.4066 28.2556 28.8101 27.8876 29.2844 27.671C29.9371 27.3728 30.6009 27.3739 31.3204 27.3752Z" fill="white" />
</svg>
<span class="d-block mt-2">WhatsApp</span>
</a>
@endif
</div>
@endif
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,42 @@
<table class="table" id="datatable">
<thead>
<tr>
<th>{{ __('SL') }}.</th>
<th>{{ __('Name') }}</th>
<th>{{ __('Email') }}</th>
<th>{{ __('Phone') }}</th>
<th>{{ __('Type') }}</th>
<th>{{ __('Due Amount') }}</th>
</tr>
</thead>
<tbody>
@foreach ($parties as $party)
<tr>
<td>{{ $loop->iteration }}</td>
<td>{{ $party->name }}</td>
<td>{{ $party->email }}</td>
<td>{{ $party->phone }}</td>
@if ($party->type == 'Retailer')
<td>{{ __('Customer') }}</td>
@else
<td>{{ $party->type }}</td>
@endif
<td class="text-danger text-end">
{{ currency_format($party->due, currency: business_currency()) }}
</td>
</tr>
@endforeach
</tbody>
@if ($parties->count() > 0)
<tr class="table-footer">
<td class="text-start">{{ __('Total') }}</td>
<td class="d-print-none"></td>
<td></td>
<td></td>
<td></td>
<td class="text-end">
{{ currency_format($parties->sum('due'), currency: business_currency()) }}
</td>
</tr>
@endif
</table>

View File

@@ -0,0 +1,64 @@
@extends('layouts.business.pdf.pdf_layout')
@section('pdf_title')
<div class="table-header justify-content-center border-0 d-none d-block d-print-block text-center">
@include('business::print.header')
<h4 style="text-align: center; margin: 0; padding: 0; font-size: 16px;" class="">{{ __('Due List') }}</h4>
</div>
@endsection
@section('pdf_content')
<table width="100%" cellpadding="6" cellspacing="0"
style="border-collapse: collapse; border: 1px solid gainsboro; font-size:12px;"
id="datatable">
<thead>
<tr style="background-color: #C52127; color: white;">
<th style="font-size:12px; border:1px solid gainsboro; color: white" class="text-start">{{ __('Name') }}</th>
<th style="font-size:12px; border:1px solid gainsboro; color: white" class="text-start">{{ __('Email') }}</th>
<th style="font-size:12px; border:1px solid gainsboro; color: white" class="text-start">{{ __('Phone') }}</th>
<th style="font-size:12px; border:1px solid gainsboro; color: white" class="text-start">{{ __('Type') }}</th>
<th style="font-size:12px; border:1px solid gainsboro; color: white" class="text-start">{{ __('Due Amount') }}</th>
</tr>
</thead>
<tbody>
@foreach ($parties as $party)
<tr style="background-color: {{ $loop->even ? '#F5F5F5' : '#FFFFFF' }};">
<td style="border:1px solid gainsboro; text-align:center;">
{{ $party->name }}
</td>
<td style="border:1px solid gainsboro; text-align:center;">
{{ $party->email }}
</td>
<td style="border:1px solid gainsboro; text-align:center;">
{{ $party->phone }}
</td>
@if ($party->type == 'Retailer')
<td style="border:1px solid gainsboro; text-align:center;">{{ __('Customer') }}</td>
@else
<td style="border:1px solid gainsboro; text-align:center;">
{{ $party->type }}
</td>
@endif
<td style="border:1px solid gainsboro; text-align:center;">
{{ currency_format($party->due, currency: business_currency()) }}
</td>
</tr>
@endforeach
</tbody>
@if ($parties->count() > 0)
<tfoot>
<tr style="background-color:#C52127; color:#FFFFFF; font-weight:bold;">
<td style="border:1px solid gainsboro; text-align:center; color: white">
{{ __('Total') }}
</td>
<td style="border:1px solid gainsboro; text-align:center;"></td>
<td style="border:1px solid gainsboro; text-align:center;"></td>
<td style="border:1px solid gainsboro; text-align:center;"></td>
<td style="border:1px solid gainsboro; text-align:center; color: white; font-weight: 600;">
{{ currency_format($parties->sum('due'), currency: business_currency()) }}
</td>
</tr>
</tfoot>
@endif
</table>
@endsection

View File

@@ -0,0 +1,360 @@
@extends('layouts.business.master')
@section('title')
{{ __('Create Product') }}
@endsection
@section('main_content')
<div class="erp-table-section">
<div class="container-fluid">
<div class="card">
<div class="table-header p-16">
<h4>{{ __('Add New Estimates') }}</h4>
</div>
<div class="order-form-section p-16">
<form method="post">
<input type="hidden" name="type" value="inventory">
<div class="row">
<!-- Customer -->
<div class="col-lg-6">
<label>{{ __('Customer') }}</label>
<div class="input-group w-100">
<div class="gpt-up-down-arrow estimate-dropdown position-relative w-100">
<select class="form-control inventory-customer-select w-100">
<option value="">{{ __('Select a Customer') }}</option>
<option>{{ __('John Doe (Retail)') }}</option>
<option>{{ __('ABC Store (Wholesale)') }}</option>
</select>
<span></span>
</div>
<button type="button"
class="btn btn-danger square-btn square-btn-2 d-flex justify-content-center align-items-center">
{{-- <img src="{{ asset("assets/images/icons/plus-2.svg") }}" alt=""> --}}
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 4.17188V15.8385" stroke="var(--clr-primary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.16602 10H15.8327" stroke="var(--clr-primary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
</div>
<!-- Invoice -->
<div class="col-lg-3">
<label>{{ __('Reference') }}.</label>
<input type="text" placeholder="Ex: 5" class="form-control"
value="" readonly>
</div>
<!-- Date -->
<div class="col-lg-3">
<label>{{ __('Quotation Number') }}</label>
<input placeholder="Ex: Q-000123" type="text" class="form-control">
</div>
<!-- Product -->
<div class="col-lg-6">
<div class="address-wrapper">
<!-- Billing -->
<div class="address-box">
<div class="address-title">
<span>Billing Address:
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_40006660_159917)">
<path d="M4.08578 9.9142L9.91423 4.08578C10.1868 3.81316 10.3231 3.67685 10.396 3.5298C10.5347 3.25002 10.5347 2.92155 10.396 2.64177C10.3231 2.49472 10.1868 2.35841 9.91423 2.08578C9.64158 1.81316 9.50528 1.67685 9.35823 1.60398C9.07843 1.46534 8.74998 1.46534 8.47018 1.60398C8.32313 1.67685 8.18683 1.81316 7.91423 2.08578L2.08579 7.9142C1.79676 8.2032 1.65224 8.34775 1.57612 8.5315C1.5 8.71525 1.5 8.91965 1.5 9.3284V10.5H2.67157C3.08033 10.5 3.2847 10.5 3.46847 10.4239C3.65224 10.3478 3.79675 10.2032 4.08578 9.9142Z" stroke="#00AE2E" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 10.5H9" stroke="#00AE2E" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.25 2.75L9.25 4.75" stroke="#00AE2E" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_40006660_159917">
<rect width="12" height="12" fill="white"/>
</clipPath>
</defs>
</svg>
</span>
<i class="fa-solid fa-pen edit-icon"></i>
</div>
<p>
Walk-In Customer, Linking Street,<br>
Phoenix, Arizona, USA
</p>
</div>
<!-- Shipping -->
<div class="address-box">
<div class="address-title">
<span>Shipping Address:
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_40006660_159917)">
<path d="M4.08578 9.9142L9.91423 4.08578C10.1868 3.81316 10.3231 3.67685 10.396 3.5298C10.5347 3.25002 10.5347 2.92155 10.396 2.64177C10.3231 2.49472 10.1868 2.35841 9.91423 2.08578C9.64158 1.81316 9.50528 1.67685 9.35823 1.60398C9.07843 1.46534 8.74998 1.46534 8.47018 1.60398C8.32313 1.67685 8.18683 1.81316 7.91423 2.08578L2.08579 7.9142C1.79676 8.2032 1.65224 8.34775 1.57612 8.5315C1.5 8.71525 1.5 8.91965 1.5 9.3284V10.5H2.67157C3.08033 10.5 3.2847 10.5 3.46847 10.4239C3.65224 10.3478 3.79675 10.2032 4.08578 9.9142Z" stroke="#00AE2E" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 10.5H9" stroke="#00AE2E" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.25 2.75L9.25 4.75" stroke="#00AE2E" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_40006660_159917">
<rect width="12" height="12" fill="white"/>
</clipPath>
</defs>
</svg>
</span>
<i class="fa-solid fa-pen edit-icon"></i>
</div>
<p>
Walk-In Customer, Linking Street,<br>
Phoenix, Arizona, USA
</p>
</div>
</div>
</div>
<div class="col-lg-3">
<label>{{ __('Currency') }}</label>
<div class="gpt-up-down-arrow position-relative w-100">
<select class="form-control">
<option>{{ __('USD $') }}</option>
</select>
<span></span>
</div>
</div>
<div class="col-lg-3">
<label>{{ __('Warehouse') }}</label>
<div class="gpt-up-down-arrow position-relative w-100">
<select class="form-control">
<option>{{ __('Select a warehouse') }}</option>
</select>
<span></span>
</div>
</div>
<div class="col-lg-3">
<label>{{ __('Quotation Date') }}</label>
<input type="date" class="form-control">
</div>
<div class="col-lg-3">
<label>{{ __('Expire Date') }}</label>
<input type="date" class="form-control">
</div>
<div class="col-lg-3">
<label>{{ __('Discount Type') }}</label>
<div class="gpt-up-down-arrow position-relative w-100">
<select class="form-control">
<option>{{ __('Percentage % ') }}</option>
</select>
<span></span>
</div>
</div>
<div class="col-lg-3">
<label>{{ __('Status') }}</label>
<div class="gpt-up-down-arrow position-relative w-100">
<select class="form-control">
<option>{{ __('Status') }}</option>
</select>
<span></span>
</div>
</div>
<div class="col-lg-9">
<label>{{ __('Select Product') }}</label>
<div class="input-group w-100">
<div class="gpt-up-down-arrow estimate-dropdown position-relative w-100">
<select class="form-control inventory-customer-select w-100">
<option value="">{{ __('Search product by name') }}</option>
</select>
<span></span>
</div>
<button type="button"
class="btn btn-danger square-btn d-flex justify-content-center align-items-center">
<img src="{{ asset("assets/images/icons/barcode1.svg") }}" alt="">
</button>
</div>
</div>
<div class="col-lg-3">
<label>{{ __('Category') }}</label>
<div class="gpt-up-down-arrow position-relative w-100">
<select class="form-control">
<option>{{ __('Select a category') }}</option>
</select>
<span></span>
</div>
</div>
<!-- Left Amount -->
<div class="col-sm-12 col-md-7 col-lg-7 mt-5">
{{-- <div class="estimate-form-wrapper">
<div class="estimate-form-group">
<span class="estimate-form-label">Term & Condition</span>
<textarea
class="estimate-form-textarea"
placeholder="Enter Term & Condition"
rows="2"></textarea>
</div>
<div class="estimate-form-group">
<span class="estimate-form-label">Note</span>
<textarea
class="estimate-form-textarea"
placeholder="Enter note"
rows="2"></textarea>
</div>
</div> --}}
<div class="expense-wrapper">
<button type="button" id="addExpenseBtn" class="expense-add-btn">
+ Add Additional Expense
</button>
<div id="expenseContainer">
<!-- dynamic rows -->
</div>
<div class="estimate-form-group">
<span class="estimate-form-label">Note</span>
<textarea
class="estimate-form-textarea"
placeholder="Enter note"
rows="2"></textarea>
</div>
</div>
</div>
<!-- Right Amount -->
<div class="col-sm-12 col-md-5 col-lg-5 mt-5 sub-total-container">
<div class="payment-container mb-3 amount-info-container estimate-container inventory-amount-info-container">
<div class="mb-2 d-flex align-items-center justify-content-between">
<h6>{{ __('Sub Total') }}</h6>
<h6 class="fw-bold" id="sub_total">
{{ __('৳ 0.00') }}
</h6>
</div>
<div class="row save-amount-container align-items-center mb-2">
<h6 class="payment-title col-6">
{{ __('Vat') }}
</h6>
<div class="col-6 w-100 d-flex justify-content-between gap-2">
<div class="d-flex align-items-center gap-2">
<select class="form-select vat_select">
<option value="">{{ __('Select') }}</option>
<option value="1">{{ __('Standard Vat (5%)') }}</option>
<option value="2">{{ __('Reduced Vat (10%)') }}</option>
</select>
</div>
<input
type="number"
class="form-control right-start-input"
placeholder="{{ __('0') }}"
value="0"
readonly
>
</div>
</div>
<div class="row save-amount-container align-items-center mb-2">
<h6 class="payment-title col-6">
{{ __('Discount') }}
</h6>
<div class="col-6 w-100 d-flex justify-content-between gap-2">
<div class="d-flex align-items-center gap-2">
<select class="form-select discount_type">
<option value="flat">
{{ __('Flat (৳)') }}
</option>
<option value="percent">
{{ __('Percent (%)') }}
</option>
</select>
</div>
<input
type="number"
class="form-control right-start-input"
placeholder="{{ __('0') }}"
value="0"
>
</div>
</div>
<div class="shopping-crg-grid mb-2">
<h6 class="payment-title">
{{ __('Shipping Charge') }}
</h6>
<input
type="number"
class="form-control right-start-input"
placeholder="{{ __('0') }}"
value="0"
>
</div>
<div class="d-flex align-items-center justify-content-between fw-bold">
<div>{{ __('Total Amount') }}</div>
<h6 class="fw-bold" id="total_amount">
{{ __('৳ 0.00') }}
</h6>
</div>
</div>
</div>
<!-- Buttons -->
<div class="col-lg-12 text-center mt-4">
<button class="theme-btn border-btn m-2">
{{ __('Reset') }}
</button>
<button class="theme-btn m-2">
{{ __('Save & Print') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
@push('js')
<script>
const addExpenseBtn = document.getElementById("addExpenseBtn");
const expenseContainer = document.getElementById("expenseContainer");
addExpenseBtn.addEventListener("click", () => {
const row = document.createElement("div");
row.className = "expense-row";
row.innerHTML = `
<input type="text" class="form-control" placeholder="Enter expense name">
<input type="text" class="form-control" placeholder="Ex: $150">
<button type="button" class="expense-delete-btn">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.25 4.58398L15.7336 12.9382C15.6016 15.0727 15.5357 16.1399 15.0007 16.9072C14.7361 17.2866 14.3956 17.6067 14.0006 17.8473C13.2017 18.334 12.1325 18.334 9.99392 18.334C7.8526 18.334 6.78192 18.334 5.98254 17.8464C5.58733 17.6054 5.24667 17.2847 4.98223 16.9047C4.4474 16.1362 4.38287 15.0674 4.25384 12.93L3.75 4.58398" stroke="#C52127" stroke-width="1.25" stroke-linecap="round"/>
<path d="M7.5 9.7793H12.5" stroke="#C52127" stroke-width="1.25" stroke-linecap="round"/>
<path d="M8.75 13.0449H11.25" stroke="#C52127" stroke-width="1.25" stroke-linecap="round"/>
<path d="M2.5 4.58268H17.5M13.3796 4.58268L12.8107 3.40912C12.4328 2.62957 12.2438 2.23978 11.9179 1.99669C11.8457 1.94277 11.7691 1.8948 11.689 1.85327C11.3281 1.66602 10.8949 1.66602 10.0286 1.66602C9.1405 1.66602 8.6965 1.66602 8.32957 1.86112C8.24826 1.90436 8.17066 1.95427 8.09758 2.01032C7.76787 2.26327 7.5837 2.66731 7.21535 3.4754L6.71061 4.58268" stroke="#C52127" stroke-width="1.25" stroke-linecap="round"/>
</svg>
</button>
`;
row.querySelector(".expense-delete-btn").addEventListener("click", () => {
row.remove();
});
expenseContainer.appendChild(row);
});
</script>
@endpush

View File

@@ -0,0 +1,352 @@
@extends('layouts.business.master')
@section('title')
{{ __('Estimate List') }}
@endsection
@section('main_content')
<div class="erp-table-section">
<div class="container-fluid">
<div class="card">
<div class="card-bodys">
<!-- Header -->
<div class="table-header p-16 d-print-none">
<h4>{{ __('Estimate List') }}</h4>
<div class="d-flex align-items-center gap-3 flex-wrap">
<a href="#"
class="bulk-import-container">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 10V12.6667C14 13.0203 13.8595 13.3594 13.6095 13.6095C13.3594 13.8595 13.0203 14 12.6667 14H3.33333C2.97971 14 2.64057 13.8595 2.39052 13.6095C2.14048 13.3594 2 13.0203 2 12.6667V10" stroke="#4B5563" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.3346 5.33333L8.0013 2L4.66797 5.33333" stroke="#4B5563" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 2V10" stroke="#4B5563" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
{{ __('Bulk Import') }}
</a>
<a href="#"
class="add-order-btn rounded-2 active btn btn-primary">
<i class="fas fa-plus-circle me-1"></i>
{{ __('Add Estimates') }}
</a>
</div>
</div>
<div class="table-header justify-content-center border-0 text-center d-none d-print-block">
<div class="print-header">
<!-- static print header -->
</div>
<h4 class="mt-2">{{ __('Product List') }}</h4>
</div>
<div class="table-top-form p-16">
<form action="#" method="GET" class="filter-form" table="#product-data">
<div class="table-top-left d-flex gap-3 flex-wrap">
<div class="gpt-up-down-arrow position-relative d-print-none">
<select name="per_page" class="form-control">
<option value="20">{{ __('Show 20') }}</option>
<option value="50">{{ __('Show 50') }}</option>
<option value="100">{{ __('Show 100') }}</option>
<option value="500">{{ __('Show 500') }}</option>
</select>
<span></span>
</div>
<div class="gpt-up-down-arrow position-relative d-print-none">
<select name="per_page" class="form-control">
<option value="20">{{ __('Filter') }}</option>
</select>
<span></span>
</div>
<div class="gpt-up-down-arrow position-relative d-print-none warehouse-select">
<select name="per_page" class="form-control">
<option value="20">{{ __('All Warehouse ') }}</option>
</select>
<span></span>
</div>
<div class="table-search position-relative d-print-none">
<input
class="form-control searchInput"
type="text"
name="search"
placeholder="{{ __('Search...') }}"
value=""
>
<span class="position-absolute">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M14.582 14.582L18.332 18.332"
stroke="#4D4D4D" stroke-width="1.25"
stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16.668 9.168C16.668 5.026 13.31 1.668 9.168 1.668
C5.026 1.668 1.668 5.026 1.668 9.168
C1.668 13.31 5.026 16.668 9.168 16.668
C13.31 16.668 16.668 13.31 16.668 9.168Z"
stroke="#4D4D4D" stroke-width="1.25"
stroke-linejoin="round"/>
</svg>
</span>
</div>
</div>
</form>
<div class="table-top-btn-group d-print-none">
<ul>
<li>
<a href="#">
<img src={{ asset("assets/images/logo/csv.svg") }} alt="csv">
</a>
</li>
<li>
<a href="#">
<img src={{ asset("assets/images/logo/excel.svg") }} alt="excel">
</a>
</li>
<li>
<a href="#" target="_blank">
<img src={{ asset("assets/images/logo/pdf.svg") }} alt="pdf">
</a>
</li>
<li>
<a onclick="window.print()" class="print-window">
<img src={{ asset("assets/images/logo/printer.svg") }} alt="print">
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="delete-item delete-show d-none">
<div class="delete-item-show">
<p class="fw-bold">
<span class="selected-count">0</span> {{ __('items show') }}
</p>
<button type="button">
{{ __('Delete') }}
</button>
</div>
</div>
<!-- Table Data Wrapper -->
<div id="product-data">
<div class="responsive-table m-0">
<table class="table" id="datatable">
<thead>
<tr>
<th class="w-60 d-print-none">
<div class="d-flex align-items-center gap-3">
<input type="checkbox" class="select-all-delete multi-delete">
</div>
</th>
<th class="d-print-none">SL.</th>
<th>Invoice</th>
<th>Customer Name</th>
<th>Phone Number</th>
<th>Warehouse</th>
<th>Qty</th>
<th>Amount</th>
<th>Create</th>
<th>Expiry Date</th>
<th>Status</th>
<th class="d-print-none">Action</th>
</tr>
</thead>
<tbody id="product-data">
<tr>
<td class="w-60 checkbox d-print-none">
<input type="checkbox" class="delete-checkbox-item multi-delete">
</td>
<td class="d-print-none">1</td>
<td>Ph2563</td>
<td>Ronald Richards</td>
<td>(208) 555-0112</td>
<td>Warehouse 1</td>
<td>5</td>
<td>$500</td>
<td>6 Jun 2025</td>
<td>6 July 2025</td>
<td>
<span class="estimate-badge estimate-badge-draft">Draft</span>
</td>
<td class="d-print-none">
<div class="dropdown table-action">
<button type="button" data-bs-toggle="dropdown">
<i class="far fa-ellipsis-v"></i>
</button>
<ul class="dropdown-menu">
<li>
<a href="#parties-view" data-bs-toggle="modal">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.158 8.28375C16.386 8.60348 16.5 8.76338 16.5 9C16.5 9.23662 16.386 9.39652 16.158 9.71625C15.1334 11.1529 12.5169 14.25 9 14.25C5.48309 14.25 2.86657 11.1529 1.84203 9.71625C1.61401 9.39652 1.5 9.23662 1.5 9C1.5 8.76338 1.61401 8.60348 1.84203 8.28375C2.86657 6.84708 5.48309 3.75 9 3.75C12.5169 3.75 15.1334 6.84708 16.158 8.28375Z" stroke="#4B5563" stroke-width="1.2"/>
<path d="M11.25 9C11.25 7.75732 10.2427 6.75 9 6.75C7.75732 6.75 6.75 7.75732 6.75 9C6.75 10.2427 7.75732 11.25 9 11.25C10.2427 11.25 11.25 10.2427 11.25 9Z" stroke="#4B5563" stroke-width="1.2"/>
</svg>
View
</a>
</li>
<li>
<a href="#">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.1606 3.73679L13.2119 2.68547C13.7925 2.10484 14.7339 2.10484 15.3145 2.68547C15.8951 3.2661 15.8951 4.20748 15.3145 4.78811L14.2632 5.83943M12.1606 3.73679L8.23515 7.66222C7.4512 8.4462 7.05919 8.83815 6.79228 9.31582C6.52535 9.7935 6.2568 10.9214 6 12C7.07857 11.7432 8.2065 11.4746 8.68418 11.2077C9.16185 10.9408 9.5538 10.5488 10.3378 9.76485L14.2632 5.83943M12.1606 3.73679L14.2632 5.83943" stroke="#4B5563" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15.75 9C15.75 12.1819 15.75 13.773 14.7615 14.7615C13.773 15.75 12.1819 15.75 9 15.75C5.81802 15.75 4.22703 15.75 3.23851 14.7615C2.25 13.773 2.25 12.1819 2.25 9C2.25 5.81802 2.25 4.22703 3.23851 3.23851C4.22703 2.25 5.81802 2.25 9 2.25" stroke="#4B5563" stroke-width="1.2" stroke-linecap="round"/>
</svg>
Edit
</a>
</li>
<li>
<a href="#">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_40001163_47282)">
<path d="M8.24805 7.5H8.2593M8.25082 12H8.26207" stroke="#4B5563" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.25 9.75H11.25" stroke="#4B5563" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.125 3.75C13.7463 3.75 14.25 4.25368 14.25 4.875C14.25 5.49632 13.7463 6 13.125 6C12.5037 6 12 5.49632 12 4.875C12 4.25368 12.5037 3.75 13.125 3.75Z" stroke="#4B5563" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.08067 8.35792C1.32831 9.19822 1.31213 10.4659 2.00262 11.3578C3.37283 13.1275 4.87256 14.6272 6.64225 15.9973C7.53405 16.6879 8.80178 16.6717 9.64208 15.9193C11.9234 13.8766 14.0126 11.7419 16.0289 9.39592C16.2283 9.16402 16.3529 8.87978 16.3809 8.5752C16.5047 7.22849 16.7589 3.3485 15.7052 2.2948C14.6515 1.24111 10.7715 1.49533 9.4248 1.61907C9.12023 1.64706 8.83598 1.77175 8.604 1.97108C6.25809 3.98734 4.12336 6.07658 2.08067 8.35792Z" stroke="#4B5563" stroke-width="1.25"/>
</g>
<defs>
<clipPath id="clip0_40001163_47282">
<rect width="18" height="18" fill="white"/>
</clipPath>
</defs>
</svg>
Pay Now
</a>
</li>
<li>
<a href="#" class="confirm-action">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_40001163_47290)">
<path d="M5.51547 13.5C3.92313 13.5 3.12696 13.5 2.56012 13.1601C2.18965 12.9379 1.89119 12.6203 1.69856 12.2431C1.40384 11.666 1.48307 10.903 1.64151 9.37687C1.77377 8.10292 1.83991 7.46591 2.16513 6.99811C2.37865 6.69097 2.66744 6.43923 3.00598 6.26515C3.52161 6 4.18623 6 5.51547 6H12.4845C13.8138 6 14.4784 6 14.994 6.26515C15.3326 6.43923 15.6214 6.69097 15.8349 6.99811C16.1601 7.46591 16.2263 8.10292 16.3585 9.37687C16.517 10.903 16.5962 11.666 16.3014 12.2431C16.1088 12.6203 15.8104 12.9379 15.4399 13.1601C14.873 13.5 14.0769 13.5 12.4845 13.5" stroke="#4B5563" stroke-width="1.25"/>
<path d="M12.75 6V4.5C12.75 3.08579 12.75 2.37868 12.3106 1.93934C11.8713 1.5 11.1642 1.5 9.75 1.5H8.25C6.83578 1.5 6.12868 1.5 5.68934 1.93934C5.25 2.37868 5.25 3.08579 5.25 4.5V6" stroke="#4B5563" stroke-width="1.25" stroke-linejoin="round"/>
<path d="M10.4915 12H7.50848C6.99452 12 6.73753 12 6.51887 12.0817C6.22733 12.1905 5.9777 12.402 5.80965 12.6824C5.68361 12.8928 5.62129 13.1633 5.49663 13.7043C5.30185 14.5496 5.20446 14.9722 5.27069 15.3112C5.35901 15.763 5.63428 16.1455 6.01689 16.348C6.30385 16.5 6.70539 16.5 7.50848 16.5H10.4915C11.2946 16.5 11.6962 16.5 11.9831 16.348C12.3657 16.1455 12.641 15.763 12.7293 15.3112C12.7955 14.9722 12.6982 14.5496 12.5034 13.7043C12.3788 13.1633 12.3164 12.8928 12.1904 12.6824C12.0223 12.402 11.7727 12.1905 11.4812 12.0817C11.2625 12 11.0055 12 10.4915 12Z" stroke="#4B5563" stroke-width="1.25" stroke-linejoin="round"/>
<path d="M13.5 9H13.5075" stroke="#4B5563" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_40001163_47290">
<rect width="18" height="18" fill="white"/>
</clipPath>
</defs>
</svg>
Print
</a>
</li>
<li>
<a href="#" class="confirm-action">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.625 4.125L14.1602 11.6438C14.0414 13.5648 13.9821 14.5253 13.5006 15.2159C13.2625 15.5573 12.956 15.8455 12.6005 16.062C11.8816 16.5 10.9192 16.5 8.99452 16.5C7.06734 16.5 6.10372 16.5 5.38429 16.0612C5.0286 15.8443 4.722 15.5556 4.48401 15.2136C4.00266 14.5219 3.94459 13.5601 3.82846 11.6364L3.375 4.125" stroke="#4B5563" stroke-width="1.2" stroke-linecap="round"/>
<path d="M6.75 8.79688H11.25" stroke="#4B5563" stroke-width="1.2" stroke-linecap="round"/>
<path d="M7.875 11.7422H10.125" stroke="#4B5563" stroke-width="1.2" stroke-linecap="round"/>
<path d="M2.25 4.125H15.75M12.0416 4.125L11.5297 3.0688C11.1896 2.3672 11.0195 2.01639 10.7261 1.79761C10.6611 1.74908 10.5922 1.7059 10.5201 1.66852C10.1953 1.5 9.80542 1.5 9.02572 1.5C8.22645 1.5 7.82685 1.5 7.49662 1.67559C7.42343 1.71451 7.35359 1.75943 7.28783 1.80988C6.99109 2.03753 6.82533 2.40116 6.49381 3.12844L6.03955 4.125" stroke="#4B5563" stroke-width="1.2" stroke-linecap="round"/>
</svg>
Delete
</a>
</li>
</ul>
</div>
</td>
</tr>
<tr>
<td class="w-60 checkbox d-print-none">
<input type="checkbox" class="delete-checkbox-item multi-delete">
</td>
<td class="d-print-none">2</td>
<td>Ph2563</td>
<td>Eleanor Pena</td>
<td>(225) 555-0118</td>
<td>Warehouse 2</td>
<td class="text-danger">10</td>
<td>$800</td>
<td>8 Jun 2025</td>
<td class="text-danger">8 Jun 2025</td>
<td>
<span class="estimate-badge estimate-badge-expired">Expired</span>
</td>
<td class="d-print-none">
<div class="dropdown table-action">
<button type="button">
<i class="far fa-ellipsis-v"></i>
</button>
</div>
</td>
</tr>
<tr>
<td class="w-60 checkbox d-print-none">
<input type="checkbox" class="delete-checkbox-item multi-delete">
</td>
<td class="d-print-none">3</td>
<td>Ph2563</td>
<td>Dianne Russell</td>
<td>(307) 555-0133</td>
<td>Warehouse 1</td>
<td>7</td>
<td>$300</td>
<td>12 Jun 2025</td>
<td>6 July 2025</td>
<td>
<span class="estimate-badge estimate-badge-sent">Sent</span>
</td>
<td class="d-print-none">
<div class="dropdown table-action">
<button type="button">
<i class="far fa-ellipsis-v"></i>
</button>
</div>
</td>
</tr>
<tr>
<td class="w-60 checkbox d-print-none">
<input type="checkbox" class="delete-checkbox-item multi-delete">
</td>
<td class="d-print-none">4</td>
<td>Ph2563</td>
<td>Cameron Williamson</td>
<td>(270) 555-0117</td>
<td>Warehouse 2</td>
<td>3</td>
<td>$750</td>
<td>15 Jun 2025</td>
<td>6 July 2025</td>
<td>
<span class="estimate-badge estimate-badge-declined">Declined</span>
</td>
<td class="d-print-none">
<div class="dropdown table-action">
<button type="button">
<i class="far fa-ellipsis-v"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,68 @@
@extends('layouts.business.pdf.pdf_layout')
@section('pdf_title')
<div class="table-header justify-content-center border-0 d-none d-block d-print-block text-center">
@include('business::print.header')
<h4 style="text-align: center; margin: 0; padding: 0; font-size: 16px;" class="">{{ __('Expired Product List') }}</h4>
</div>
@endsection
@section('pdf_content')
<table width="100%" cellpadding="6" cellspacing="0"
style="border-collapse: collapse; border: 1px solid gainsboro; font-size:12px;"
id="datatable">
<thead>
<tr style="background-color: #C52127; color: white;">
<th style="font-size:12px; border:1px solid gainsboro; color: white" class="text-start">{{ __('Product Name') }}</th>
<th style="font-size:12px; border:1px solid gainsboro; color: white" class="text-start">{{ __('Product Code') }}</th>
<th style="font-size:12px; border:1px solid gainsboro; color: white" class="text-start">{{ __('Category') }}</th>
<th style="font-size:12px; border:1px solid gainsboro; color: white" class="text-start">{{ __('Expired Date') }}</th>
<th style="font-size:12px; border:1px solid gainsboro; color: white" class="text-start">{{ __('Stock') }}</th>
@usercan('expired-product-reports.price')
<th style="font-size:12px; border:1px solid gainsboro; color: white" class="text-start">{{ __('Purchase Price') }}</th>
@endusercan
<th style="font-size:12px; border:1px solid gainsboro; color: white" class="text-start">{{ __('Sale Price') }}</th>
</tr>
</thead>
<tbody>
@foreach ($expired_products as $product)
@php
$nonEmptyStock = $product->stocks->firstWhere('productStock', '>', 0);
$fallbackStock = $product->stocks->first(); // fallback if no stock > 0
$stock = $nonEmptyStock ?? $fallbackStock;
$latestPurchasePrice = $stock?->productPurchasePrice ?? 0;
$latestSalePrice = $stock?->productSalePrice ?? 0;
@endphp
<tr style="background-color: {{ $loop->even ? '#F5F5F5' : '#FFFFFF' }};">
<td style="border:1px solid gainsboro; text-align:center;">
{{ $product->productName }}
</td>
<td style="border:1px solid gainsboro; text-align:center;">
{{ $product->productCode }}
</td>
<td style="border:1px solid gainsboro; text-align:center;">
{{ $product->category->categoryName ?? '' }}
</td>
<td style="border:1px solid gainsboro; text-align:center;">
@if ($product->stocks->isNotEmpty() && $product->stocks->first()->expire_date)
{{ formatted_date($product->stocks->first()->expire_date) }}
@else
{{__('N/A')}}
@endif
</td>
<td style="border:1px solid gainsboro; text-align:center;">
{{ $product->total_stock }}
</td>
@usercan('expired-product-reports.price')
<td style="border:1px solid gainsboro; text-align:center;">
{{ currency_format($latestPurchasePrice, currency: business_currency()) }}
</td>
@endusercan
<td style="border:1px solid gainsboro; text-align:center;">
{{ currency_format($latestSalePrice, currency: business_currency()) }}
</td>
</tr>
@endforeach
</tbody>
</table>
@endsection

View File

@@ -0,0 +1,59 @@
@extends('layouts.business.master')
@section('title')
{{ $party->type === 'Supplier' ? __('Advance Pay') : __('Advance Collect') }}
@endsection
@section('main_content')
<div class="erp-table-section">
<div class="container-fluid">
<div class="card border-0">
<div class="card-bodys ">
<div class="table-header p-16">
<h4>{{ $party->type === 'Supplier' ? __('Advance Pay') : __('Advance Collect') }}</h4>
</div>
<div class="order-form-section p-16">
<form action="{{ route('business.parties.advance-payments.store') }}" method="POST" class="ajaxform">
@csrf
<input type="hidden" name="party_id" value="{{ $party->id }}">
<div class="add-suplier-modal-wrapper d-block">
<div class="row">
<div class="col-lg-6 mb-2">
<label>{{ $party->type == 'Supplier' ? __('Supplier Name') : __('Customer Name') }}</label>
<input type="text" value="{{ $party->name }}" readonly class="form-control">
</div>
<div class="col-lg-6 mb-2">
<label>{{ __('Date') }}</label>
<input type="date" name="date" required class="form-control" value="{{ date('Y-m-d') }}">
</div>
<div class="col-lg-6 mb-2">
<label>{{ __('Wallet Amount') }}</label>
<input type="number" value="{{ $party->wallet }}" readonly class="form-control">
</div>
<div class="col-lg-6 mb-2">
<label>{{ __('Advance Amount') }}</label>
<input type="number" name="amount" id="advanceAmount" required class="form-control" placeholder="{{ __('Enter amount') }}">
</div>
<div class="col-lg-6 mb-2">
<label>{{ __('Notes') }}</label>
<input type="text" name="note" class="form-control" placeholder="{{ __('Enter notes') }}">
</div>
<div class="col-lg-6 mb-2">
<label>{{ __('Payment Type') }}</label>
@include('business::component.payment_type', ['context' => $party->type === 'Supplier' ? 'advance_pay' : 'advance_collect', 'payment_types' => $payment_types])
</div>
<div class="col-lg-12">
<div class="button-group text-center mt-5">
<button type="reset" class="theme-btn border-btn m-2">{{ __('Reset') }}</button>
<button class="theme-btn m-2 submit-btn">{{ __('Save') }}</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

Some files were not shown because too many files have changed in this diff Show More