feat: 實作應付帳款與銷售訂單權限管理與進貨單權限修正
All checks were successful
ERP-Deploy-Demo / deploy-demo (push) Successful in 1m17s

This commit is contained in:
2026-02-24 17:29:09 +08:00
parent 455f945296
commit e406ecd63d
8 changed files with 57 additions and 33 deletions

View File

@@ -193,7 +193,9 @@ class RoleController extends Controller
'production_orders' => '生產工單管理', 'production_orders' => '生產工單管理',
'utility_fees' => '公共事業費管理', 'utility_fees' => '公共事業費管理',
'accounting' => '會計報表', 'accounting' => '會計報表',
'account_payables' => '應付帳款',
'sales_imports' => '銷售單匯入管理', 'sales_imports' => '銷售單匯入管理',
'sales_orders' => '銷售訂單管理',
'store_requisitions' => '門市叫貨申請', 'store_requisitions' => '門市叫貨申請',
'users' => '使用者管理', 'users' => '使用者管理',
'roles' => '角色與權限', 'roles' => '角色與權限',

View File

@@ -9,11 +9,17 @@ use App\Modules\Finance\Controllers\AccountPayableController;
Route::middleware('auth')->group(function () { Route::middleware('auth')->group(function () {
// 應付帳款 // 應付帳款
Route::group(['prefix' => 'finance'], function () { Route::group(['prefix' => 'finance'], function () {
Route::middleware('permission:account_payables.view')->group(function () {
Route::get('/account-payables', [AccountPayableController::class, 'index'])->name('account-payables.index'); Route::get('/account-payables', [AccountPayableController::class, 'index'])->name('account-payables.index');
Route::get('/account-payables/{accountPayable}', [AccountPayableController::class, 'show'])->name('account-payables.show'); Route::get('/account-payables/{accountPayable}', [AccountPayableController::class, 'show'])->name('account-payables.show');
});
Route::middleware('permission:account_payables.edit')->group(function () {
Route::post('/account-payables/{accountPayable}/invoice', [AccountPayableController::class, 'updateInvoice'])->name('account-payables.invoice'); Route::post('/account-payables/{accountPayable}/invoice', [AccountPayableController::class, 'updateInvoice'])->name('account-payables.invoice');
});
Route::middleware('permission:account_payables.pay')->group(function () {
Route::post('/account-payables/{accountPayable}/pay', [AccountPayableController::class, 'pay'])->name('account-payables.pay'); Route::post('/account-payables/{accountPayable}/pay', [AccountPayableController::class, 'pay'])->name('account-payables.pay');
}); });
});
// 公共事業費管理 // 公共事業費管理
Route::middleware('permission:utility_fees.view')->group(function () { Route::middleware('permission:utility_fees.view')->group(function () {

View File

@@ -5,7 +5,9 @@ use Illuminate\Support\Facades\Route;
Route::middleware(['web', 'auth', 'verified'])->group(function () { Route::middleware(['web', 'auth', 'verified'])->group(function () {
Route::prefix('integration')->name('integration.')->group(function () { Route::prefix('integration')->name('integration.')->group(function () {
Route::middleware('permission:sales_orders.view')->group(function () {
Route::get('sales-orders', [SalesOrderController::class, 'index'])->name('sales-orders.index'); Route::get('sales-orders', [SalesOrderController::class, 'index'])->name('sales-orders.index');
Route::get('sales-orders/{salesOrder}', [SalesOrderController::class, 'show'])->name('sales-orders.show'); Route::get('sales-orders/{salesOrder}', [SalesOrderController::class, 'show'])->name('sales-orders.show');
}); });
}); });
});

View File

@@ -185,7 +185,7 @@ class GoodsReceiptController extends Controller
public function submit(GoodsReceipt $goodsReceipt) public function submit(GoodsReceipt $goodsReceipt)
{ {
if (!auth()->user()->can('goods_receipts.update')) { if (!auth()->user()->can('goods_receipts.edit')) {
return back()->with('error', '您沒有權限確認點收'); return back()->with('error', '您沒有權限確認點收');
} }

View File

@@ -183,7 +183,7 @@ Route::middleware('auth')->group(function () {
// 點收提交路由 // 點收提交路由
Route::post('/goods-receipts/{goods_receipt}/submit', [\App\Modules\Inventory\Controllers\GoodsReceiptController::class, 'submit']) Route::post('/goods-receipts/{goods_receipt}/submit', [\App\Modules\Inventory\Controllers\GoodsReceiptController::class, 'submit'])
->middleware('permission:goods_receipts.update') ->middleware('permission:goods_receipts.edit')
->name('goods-receipts.submit'); ->name('goods-receipts.submit');
Route::delete('/goods-receipts/{goods_receipt}', [\App\Modules\Inventory\Controllers\GoodsReceiptController::class, 'destroy']) Route::delete('/goods-receipts/{goods_receipt}', [\App\Modules\Inventory\Controllers\GoodsReceiptController::class, 'destroy'])

View File

@@ -65,7 +65,7 @@ class PermissionSeeder extends Seeder
// 進貨單管理 // 進貨單管理
'goods_receipts.view' => '檢視', 'goods_receipts.view' => '檢視',
'goods_receipts.create' => '建立', 'goods_receipts.create' => '建立',
'goods_receipts.edit' => '編輯', 'goods_receipts.edit' => '確認點收',
'goods_receipts.delete' => '刪除', 'goods_receipts.delete' => '刪除',
// 出貨單管理 (Delivery Notes / Shipping Orders) // 出貨單管理 (Delivery Notes / Shipping Orders)
@@ -126,6 +126,11 @@ class PermissionSeeder extends Seeder
'accounting.view' => '檢視', 'accounting.view' => '檢視',
'accounting.export' => '匯出', 'accounting.export' => '匯出',
// 應付帳款管理
'account_payables.view' => '檢視',
'account_payables.edit' => '登記發票',
'account_payables.pay' => '標記付款',
// 銷售匯入管理 // 銷售匯入管理
'sales_imports.view' => '檢視', 'sales_imports.view' => '檢視',
'sales_imports.create' => '建立', 'sales_imports.create' => '建立',
@@ -184,6 +189,7 @@ class PermissionSeeder extends Seeder
'system.view_logs', 'system.view_logs',
'utility_fees.view', 'utility_fees.create', 'utility_fees.edit', 'utility_fees.delete', 'utility_fees.view', 'utility_fees.create', 'utility_fees.edit', 'utility_fees.delete',
'accounting.view', 'accounting.export', 'accounting.view', 'accounting.export',
'account_payables.view', 'account_payables.edit', 'account_payables.pay',
'sales_imports.view', 'sales_imports.create', 'sales_imports.confirm', 'sales_imports.delete', 'sales_imports.view', 'sales_imports.create', 'sales_imports.confirm', 'sales_imports.delete',
'store_requisitions.view', 'store_requisitions.create', 'store_requisitions.edit', 'store_requisitions.view', 'store_requisitions.create', 'store_requisitions.edit',
'store_requisitions.delete', 'store_requisitions.approve', 'store_requisitions.cancel', 'store_requisitions.delete', 'store_requisitions.approve', 'store_requisitions.cancel',
@@ -225,6 +231,7 @@ class PermissionSeeder extends Seeder
'utility_fees.view', 'utility_fees.view',
'inventory_report.view', 'inventory_report.view',
'accounting.view', 'accounting.view',
'account_payables.view',
'sales_orders.view', 'sales_orders.view',
]); ]);

View File

@@ -10,6 +10,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogD
import { Input } from '@/Components/ui/input'; import { Input } from '@/Components/ui/input';
import { Label } from '@/Components/ui/label'; import { Label } from '@/Components/ui/label';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { Can } from '@/Components/Permission/Can';
const getStatusBadgeVariant = (status: string) => { const getStatusBadgeVariant = (status: string) => {
switch (status) { switch (status) {
@@ -117,6 +118,7 @@ export default function AccountPayableShow({ payable }: any) {
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Can permission="account_payables.edit">
<Button <Button
variant="outline" variant="outline"
className="gap-2 button-outlined-primary" className="gap-2 button-outlined-primary"
@@ -125,8 +127,10 @@ export default function AccountPayableShow({ payable }: any) {
<FileText className="h-4 w-4" /> <FileText className="h-4 w-4" />
</Button> </Button>
</Can>
{payable.status !== 'paid' && ( {payable.status !== 'paid' && (
<Can permission="account_payables.pay">
<Button <Button
className="gap-2 button-filled-primary" className="gap-2 button-filled-primary"
onClick={() => setPaymentDialogOpen(true)} onClick={() => setPaymentDialogOpen(true)}
@@ -134,6 +138,7 @@ export default function AccountPayableShow({ payable }: any) {
<CheckCircle className="h-4 w-4" /> <CheckCircle className="h-4 w-4" />
</Button> </Button>
</Can>
)} )}
</div> </div>
</div> </div>
@@ -225,6 +230,7 @@ export default function AccountPayableShow({ payable }: any) {
<FileText className="h-5 w-5 text-primary-main" /> <FileText className="h-5 w-5 text-primary-main" />
</h2> </h2>
<Can permission="account_payables.edit">
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
@@ -233,6 +239,7 @@ export default function AccountPayableShow({ payable }: any) {
> >
{payable.invoice_number ? '修改' : '填寫'} {payable.invoice_number ? '修改' : '填寫'}
</Button> </Button>
</Can>
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<div> <div>

View File

@@ -250,7 +250,7 @@ function GoodsReceiptActions({ receipt }: { receipt: GoodsReceipt }) {
// 權限判斷 // 權限判斷
const canView = isSuperAdmin || permissions.includes('goods_receipts.view'); const canView = isSuperAdmin || permissions.includes('goods_receipts.view');
const canEdit = isSuperAdmin || permissions.includes('goods_receipts.update'); const canEdit = isSuperAdmin || permissions.includes('goods_receipts.edit');
const canDelete = isSuperAdmin || permissions.includes('goods_receipts.delete'); const canDelete = isSuperAdmin || permissions.includes('goods_receipts.delete');
const canSubmit = canEdit || canView; const canSubmit = canEdit || canView;