feat: Invoice Changes
This commit is contained in:
parent
3f319aae4f
commit
6edadb1b04
@ -92,17 +92,26 @@ public function handle()
|
||||
// } else {
|
||||
$spend = 0;
|
||||
// }
|
||||
$managementFee = intval(str_replace(',', '', $row['management_fee'])) ?? 0;
|
||||
$mediaFee = intval(str_replace(',', '', $row['media_fee'])) ?? 0;
|
||||
$managementFeeAmount = $managementFee > 0 ? $managementFee / 1.08 : 0;
|
||||
$mediaFeeAmount = $mediaFee > 0 ? $mediaFee / 1.08 : 0;
|
||||
|
||||
$invoice = ClientInvoice::updateOrCreate(
|
||||
['invoice_no' => $row['invoice_no']],
|
||||
[
|
||||
'client_id' => $row['client_id'],
|
||||
'is_credit_card' => intval(str_replace(',', '',$row['media_fee'])) == 0 ? 1 : 0,
|
||||
'is_credit_card' => $mediaFee == 0 ? 1 : 0,
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
'management_fee' => intval(str_replace(',', '',$row['management_fee'])) ?? 0,
|
||||
'media_fee' => intval(str_replace(',', '',$row['media_fee'])) ?? 0,
|
||||
'management_fee' => $managementFee,
|
||||
'management_fee_amount' => $managementFeeAmount,
|
||||
'management_fee_tax' => $managementFee - $managementFeeAmount,
|
||||
'media_fee' => $mediaFee,
|
||||
'media_fee_amount' => $mediaFeeAmount,
|
||||
'media_fee_tax' => $mediaFee - $mediaFeeAmount,
|
||||
'tax_percent' => 8,
|
||||
'nett_amount' => intval(str_replace(',', '',$row['media_fee'])) > 0 ? intval(str_replace(',', '',$row['media_fee'])) / 1.08 : 0,
|
||||
'nett_amount' => $mediaFeeAmount,
|
||||
'total_spending' => $spend,
|
||||
]
|
||||
);
|
||||
|
||||
@ -20,6 +20,7 @@ public function handle()
|
||||
$mccCustomerId = env('GOOGLE_ADS_LOGIN_CUSTOMER_ID'); // Manager ID without dashes
|
||||
$adsService = new GoogleAdsService();
|
||||
$accounts = $adsService->listAccounts();
|
||||
Log::info('Fetched accounts from Google Ads', ['accounts' => $accounts]);
|
||||
foreach ($accounts as $account) {
|
||||
$company = Client::updateOrCreate(
|
||||
['customer_id' => $account['id']],
|
||||
|
||||
@ -36,7 +36,11 @@ public function pending(): JsonResponse
|
||||
'end_date',
|
||||
'payment_no',
|
||||
'management_fee',
|
||||
'management_fee_amount',
|
||||
'management_fee_tax',
|
||||
'media_fee',
|
||||
'media_fee_amount',
|
||||
'media_fee_tax',
|
||||
'nett_amount',
|
||||
'total_spending',
|
||||
'created_at',
|
||||
@ -72,14 +76,18 @@ public function store(Request $request): JsonResponse
|
||||
'start_date' => ['nullable', 'date'],
|
||||
'end_date' => ['nullable', 'date', 'after_or_equal:start_date'],
|
||||
'management_fee' => ['required', 'numeric', 'min:0'],
|
||||
'management_fee_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'management_fee_tax' => ['nullable', 'numeric', 'min:0'],
|
||||
'media_fee' => ['required', 'numeric', 'min:0'],
|
||||
'tax_percent' => ['nullable', 'numeric', 'min:0', 'max:100'],
|
||||
'media_fee_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'media_fee_tax' => ['nullable', 'numeric', 'min:0'],
|
||||
'total_spending' => ['nullable', 'numeric', 'min:0'],
|
||||
]);
|
||||
|
||||
$mediaFee = $validated['media_fee'];
|
||||
$taxPercent = $validated['tax_percent'] ?? 0;
|
||||
$nettAmount = $mediaFee - ($mediaFee * ($taxPercent / 100));
|
||||
|
||||
$taxPercent = (float) ($validated['tax_percent'] ?? 0);
|
||||
$nettAmount = $mediaFee / (1 + ($taxPercent / 100));
|
||||
$sqlAccCode = $this->clientLookupService->normalizeSqlAccCode($validated['sql_acc_code'] ?? null);
|
||||
$client = ! empty($validated['client_id'])
|
||||
? \App\Models\Client::find($validated['client_id'])
|
||||
@ -111,8 +119,12 @@ public function store(Request $request): JsonResponse
|
||||
'start_date' => $validated['start_date'] ?? null,
|
||||
'end_date' => $validated['end_date'] ?? null,
|
||||
'management_fee' => $validated['management_fee'],
|
||||
'management_fee_amount' => $validated['management_fee_amount'] ?? null,
|
||||
'management_fee_tax' => $validated['management_fee_tax'] ?? null,
|
||||
'media_fee' => $validated['media_fee'],
|
||||
'tax_percent' => $taxPercent,
|
||||
'media_fee_amount' => $validated['media_fee_amount'] ?? null,
|
||||
'media_fee_tax' => $validated['media_fee_tax'] ?? null,
|
||||
'tax_percent' => null,
|
||||
'nett_amount' => $nettAmount,
|
||||
'total_spending' => $validated['total_spending'] ?? null,
|
||||
]);
|
||||
@ -221,7 +233,7 @@ private function formatPreviousPayment(array $payment): array
|
||||
$sqlAccCode = $this->paymentItemSqlAccCode($paymentItem);
|
||||
$estimatedTotal = $this->paymentItemEstimatedTotal($paymentItem);
|
||||
$exact_tax = $this->paymentItemTax($paymentItem);
|
||||
$taxPercent = $this->paymentTaxPercent($paymentItem)/100 + 1;
|
||||
$taxPercent = $this->paymentTaxPercent($paymentItem) / 100 + 1;
|
||||
$amount = $usesRepeatedPaymentAmount
|
||||
? $paymentAmount * ($estimatedTotal / $estimatedItemsTotal)
|
||||
: (float) ($paymentItem['amount'] ?? 0);
|
||||
@ -250,10 +262,10 @@ private function formatPreviousPayment(array $payment): array
|
||||
'status' => $payment['status'] ?? null,
|
||||
'sql_created_at' => $payment['sql_created_at'] ?? null,
|
||||
'amount' => $payment['amount'] ?? null,
|
||||
'media_fee' => $totals['media_fee']/1.08,
|
||||
'management_fee' => $totals['management_fee']/1.08,
|
||||
'invoice_media_fee' => $totals['invoice_media_fee']/1.08,
|
||||
'invoice_management_fee' => $totals['invoice_management_fee']/1.08,
|
||||
'media_fee' => $totals['media_fee'] / 1.08,
|
||||
'management_fee' => $totals['management_fee'] / 1.08,
|
||||
'invoice_media_fee' => $totals['invoice_media_fee'] / 1.08,
|
||||
'invoice_management_fee' => $totals['invoice_management_fee'] / 1.08,
|
||||
'invoice_number' => data_get($payment, 'invoice.invoice_number'),
|
||||
];
|
||||
}
|
||||
|
||||
@ -86,7 +86,11 @@ public function store(Request $request)
|
||||
'start_date' => ['nullable', 'date'],
|
||||
'end_date' => ['nullable', 'date', 'after_or_equal:start_date'],
|
||||
'management_fee' => ['required', 'numeric', 'min:0'],
|
||||
'management_fee_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'management_fee_tax' => ['nullable', 'numeric', 'min:0'],
|
||||
'media_fee' => ['required', 'numeric', 'min:0'],
|
||||
'media_fee_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'media_fee_tax' => ['nullable', 'numeric', 'min:0'],
|
||||
'tax_percent' => ['nullable', 'numeric', 'min:0', 'max:100'],
|
||||
'total_spending' => ['nullable', 'numeric', 'min:0'],
|
||||
]);
|
||||
@ -95,8 +99,8 @@ public function store(Request $request)
|
||||
abort_unless($this->hierarchyService->canViewClient(Auth::user(), $client), 403);
|
||||
|
||||
$mediaFee = $validated['media_fee'];
|
||||
$taxPercent = $validated['tax_percent'] ?? 0;
|
||||
$nettAmount = $mediaFee - ($mediaFee * ($taxPercent / 100));
|
||||
$taxPercent = (float) ($validated['tax_percent'] ?? 0);
|
||||
$nettAmount = $mediaFee / (1 + ($taxPercent / 100));
|
||||
|
||||
$invoice = ClientInvoice::create([
|
||||
'client_id' => $validated['client_id'],
|
||||
@ -109,7 +113,11 @@ public function store(Request $request)
|
||||
'start_date' => $validated['start_date'] ?? null,
|
||||
'end_date' => $validated['end_date'] ?? null,
|
||||
'management_fee' => $validated['management_fee'],
|
||||
'management_fee_amount' => $validated['management_fee_amount'] ?? null,
|
||||
'management_fee_tax' => $validated['management_fee_tax'] ?? null,
|
||||
'media_fee' => $validated['media_fee'],
|
||||
'media_fee_amount' => $validated['media_fee_amount'] ?? null,
|
||||
'media_fee_tax' => $validated['media_fee_tax'] ?? null,
|
||||
'tax_percent' => $taxPercent,
|
||||
'nett_amount' => $nettAmount,
|
||||
'total_spending' => $validated['total_spending'] ?? null,
|
||||
@ -145,15 +153,20 @@ public function update(Request $request, ClientInvoice $invoice)
|
||||
'end_date' => ['nullable', 'date', 'after_or_equal:start_date'],
|
||||
'amount' => ['required', 'numeric', 'min:0'],
|
||||
'management_fee' => ['required', 'numeric', 'min:0'],
|
||||
'management_fee_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'management_fee_tax' => ['nullable', 'numeric', 'min:0'],
|
||||
'media_fee' => ['required', 'numeric', 'min:0'],
|
||||
'media_fee_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'media_fee_tax' => ['nullable', 'numeric', 'min:0'],
|
||||
'tax_percent' => ['nullable', 'numeric', 'min:0', 'max:100'],
|
||||
'nett_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'total_spending' => ['nullable', 'numeric', 'min:0'],
|
||||
]);
|
||||
|
||||
$managementFee = $validated['management_fee'];
|
||||
$mediaFee = $validated['media_fee'];
|
||||
$taxPercent = (float) ($validated['tax_percent'] ?? 0);
|
||||
$nettAmount = $mediaFee / (1 + ($taxPercent / 100));
|
||||
$nettAmount = $validated['nett_amount'] ?? ($mediaFee / (1 + ($taxPercent / 100)));
|
||||
|
||||
$invoice->update([
|
||||
'invoice_no' => $validated['invoice_no'],
|
||||
@ -171,6 +184,10 @@ public function update(Request $request, ClientInvoice $invoice)
|
||||
'total_spending' => $validated['total_spending'] ?? null,
|
||||
]);
|
||||
|
||||
if(empty($invoice->approved_at)) {
|
||||
$this->approvalService->approve($invoice);
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->route('google-ads.accounts.show', ['id' => $invoice->client->customer_id])
|
||||
->with('message-info', 'Invoice updated successfully.');
|
||||
@ -315,10 +332,6 @@ public function storeClient(Request $request, ClientInvoice $invoice)
|
||||
|
||||
$selectedClientIsLinked = Client::query()
|
||||
->where('id', $validated['client_id'])
|
||||
->whereHas('customers', function ($query) {
|
||||
$query->whereNotNull('sql_acc_code')
|
||||
->where('sql_acc_code', '!=', '');
|
||||
})
|
||||
->exists();
|
||||
|
||||
if ($selectedClientIsLinked) {
|
||||
|
||||
@ -250,7 +250,7 @@ private function hydrateClient(array $account): array
|
||||
'time_zone' => $account['time_zone'],
|
||||
]
|
||||
);
|
||||
|
||||
// dd($localClient);
|
||||
$localClient->load(['assignations.user', 'invoices']);
|
||||
|
||||
$assignments = $localClient->assignations
|
||||
@ -327,7 +327,11 @@ private function hydrateClient(array $account): array
|
||||
'amount' => $invoice->amount,
|
||||
'total_spend' => number_format($totalInvoiceSpend, 2, '.', ''),
|
||||
'management_fee' => $invoice->management_fee,
|
||||
'management_fee_amount' => $invoice->management_fee_amount,
|
||||
'management_fee_tax' => $invoice->management_fee_tax,
|
||||
'media_fee' => $invoice->media_fee,
|
||||
'media_fee_amount' => $invoice->media_fee_amount,
|
||||
'media_fee_tax' => $invoice->media_fee_tax,
|
||||
'tax_percent' => $invoice->tax_percent,
|
||||
'nett_amount' => $invoice->nett_amount,
|
||||
'total_spending' => $invoice->total_spending,
|
||||
@ -467,17 +471,26 @@ public function insertCSVDataToDB()
|
||||
}
|
||||
}
|
||||
}
|
||||
$managementFee = intval($row['management_fee']);
|
||||
$mediaFee = intval($row['media_fee']);
|
||||
$managementFeeAmount = $managementFee > 0 ? $managementFee / 1.08 : 0;
|
||||
$mediaFeeAmount = $mediaFee > 0 ? $mediaFee / 1.08 : 0;
|
||||
|
||||
ClientInvoice::updateOrCreate(
|
||||
['invoice_no' => $row['invoice_no']],
|
||||
[
|
||||
'client_id' => $row['client_id'],
|
||||
'is_credit_card' => intval($row['media_fee']) == 0 ? 1 : 0,
|
||||
'is_credit_card' => $mediaFee == 0 ? 1 : 0,
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
'management_fee' => intval($row['management_fee']),
|
||||
'media_fee' => intval($row['media_fee']),
|
||||
'management_fee' => $managementFee,
|
||||
'management_fee_amount' => $managementFeeAmount,
|
||||
'management_fee_tax' => $managementFee - $managementFeeAmount,
|
||||
'media_fee' => $mediaFee,
|
||||
'media_fee_amount' => $mediaFeeAmount,
|
||||
'media_fee_tax' => $mediaFee - $mediaFeeAmount,
|
||||
'tax_percent' => 8,
|
||||
'nett_amount' => intval($row['media_fee']) > 0 ? intval($row['media_fee']) / 1.08 : 0,
|
||||
'nett_amount' => $mediaFeeAmount,
|
||||
'total_spending' => $spend,
|
||||
]
|
||||
);
|
||||
|
||||
@ -64,21 +64,28 @@ public function edit(int $id): Response
|
||||
{
|
||||
$role = Role::findOrFail($id);
|
||||
|
||||
// 1. Get all permissions with their "checked" state
|
||||
$permissions = Permission::all()->map(function ($permission) use ($role) {
|
||||
$permissions = Permission::query()
|
||||
->orderBy('group')
|
||||
->orderBy('name')
|
||||
->get()
|
||||
->map(function ($permission) use ($role) {
|
||||
$group = $permission->group
|
||||
?: $permission->group_name
|
||||
?: str($permission->name)->before('.')->headline()->toString();
|
||||
|
||||
return [
|
||||
'id' => $permission->id,
|
||||
'name' => $permission->name, // e.g. "user.create"
|
||||
'name' => $permission->name,
|
||||
'group' => $group,
|
||||
'group_name' => $permission->group_name,
|
||||
'description' => $permission->description,
|
||||
'checked' => $role->hasPermissionTo($permission->name),
|
||||
];
|
||||
});
|
||||
|
||||
// 2. Group them by the prefix (the part before the dot)
|
||||
$grouped = $permissions->groupBy(function ($item) {
|
||||
return explode('.', $item['name'])[0];
|
||||
return $item['group'];
|
||||
})->map(function ($group) {
|
||||
// 3. Force it to be a sequential array so JS sees it as []
|
||||
return $group->values()->toArray();
|
||||
});
|
||||
|
||||
|
||||
@ -26,7 +26,11 @@ class ClientInvoice extends Model
|
||||
'amount',
|
||||
'tax_percent',
|
||||
'media_fee',
|
||||
'media_fee_amount',
|
||||
'media_fee_tax',
|
||||
'management_fee',
|
||||
'management_fee_amount',
|
||||
'management_fee_tax',
|
||||
'nett_amount',
|
||||
'total_spending',
|
||||
];
|
||||
@ -40,7 +44,11 @@ class ClientInvoice extends Model
|
||||
'amount' => 'decimal:2',
|
||||
'tax_percent' => 'decimal:2',
|
||||
'media_fee' => 'decimal:2',
|
||||
'media_fee_amount' => 'decimal:2',
|
||||
'media_fee_tax' => 'decimal:2',
|
||||
'management_fee' => 'decimal:2',
|
||||
'management_fee_amount' => 'decimal:2',
|
||||
'management_fee_tax' => 'decimal:2',
|
||||
'nett_amount' => 'decimal:2',
|
||||
'total_spending' => 'decimal:2',
|
||||
];
|
||||
|
||||
@ -108,7 +108,6 @@ public function listCampaigns(string $clientCustomerId): array
|
||||
{
|
||||
$client = $this->buildClient($this->loginCustomerId);
|
||||
$service = $client->getGoogleAdsServiceClient();
|
||||
|
||||
$customerId = str_replace('-', '', $clientCustomerId);
|
||||
|
||||
$query = <<<QUERY
|
||||
@ -136,6 +135,7 @@ public function listCampaigns(string $clientCustomerId): array
|
||||
]);
|
||||
|
||||
$response = $service->search($request);
|
||||
|
||||
$campaigns = [];
|
||||
foreach ($response->iterateAllElements() as $row) {
|
||||
$c = $row->getCampaign();
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
use Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets;
|
||||
use Spatie\Permission\Middleware\PermissionMiddleware;
|
||||
use Spatie\Permission\Middleware\RoleMiddleware;
|
||||
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
@ -15,6 +17,11 @@
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
$middleware->alias([
|
||||
'permission' => PermissionMiddleware::class,
|
||||
'role' => RoleMiddleware::class,
|
||||
]);
|
||||
|
||||
$middleware->encryptCookies(except: ['appearance', 'sidebar_state']);
|
||||
|
||||
$middleware->web(append: [
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if (! Schema::hasColumn('client_invoices', 'management_fee_amount')) {
|
||||
Schema::table('client_invoices', function (Blueprint $table) {
|
||||
$table->decimal('management_fee_amount', 10, 2)->nullable()->after('management_fee');
|
||||
});
|
||||
}
|
||||
|
||||
if (! Schema::hasColumn('client_invoices', 'management_fee_tax')) {
|
||||
Schema::table('client_invoices', function (Blueprint $table) {
|
||||
$table->decimal('management_fee_tax', 10, 2)->nullable()->after('management_fee_amount');
|
||||
});
|
||||
}
|
||||
|
||||
if (! Schema::hasColumn('client_invoices', 'media_fee_amount')) {
|
||||
Schema::table('client_invoices', function (Blueprint $table) {
|
||||
$table->decimal('media_fee_amount', 10, 2)->nullable()->after('media_fee');
|
||||
});
|
||||
}
|
||||
|
||||
if (! Schema::hasColumn('client_invoices', 'media_fee_tax')) {
|
||||
Schema::table('client_invoices', function (Blueprint $table) {
|
||||
$table->decimal('media_fee_tax', 10, 2)->nullable()->after('media_fee_amount');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
if (Schema::hasColumn('client_invoices', 'media_fee_tax')) {
|
||||
Schema::table('client_invoices', function (Blueprint $table) {
|
||||
$table->dropColumn('media_fee_tax');
|
||||
});
|
||||
}
|
||||
|
||||
if (Schema::hasColumn('client_invoices', 'media_fee_amount')) {
|
||||
Schema::table('client_invoices', function (Blueprint $table) {
|
||||
$table->dropColumn('media_fee_amount');
|
||||
});
|
||||
}
|
||||
|
||||
if (Schema::hasColumn('client_invoices', 'management_fee_tax')) {
|
||||
Schema::table('client_invoices', function (Blueprint $table) {
|
||||
$table->dropColumn('management_fee_tax');
|
||||
});
|
||||
}
|
||||
|
||||
if (Schema::hasColumn('client_invoices', 'management_fee_amount')) {
|
||||
Schema::table('client_invoices', function (Blueprint $table) {
|
||||
$table->dropColumn('management_fee_amount');
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -15,7 +15,9 @@ public function run(): void
|
||||
{
|
||||
// User::factory(10)->create();
|
||||
|
||||
User::firstOrCreate(
|
||||
$this->call(RoleSeeder::class);
|
||||
|
||||
$user = User::firstOrCreate(
|
||||
['email' => 'test@example.com'],
|
||||
[
|
||||
'name' => 'Test User',
|
||||
@ -23,5 +25,7 @@ public function run(): void
|
||||
'email_verified_at' => now(),
|
||||
]
|
||||
);
|
||||
|
||||
$user->assignRole('Admin');
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,12 +2,10 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Spatie\Permission\PermissionRegistrar;
|
||||
|
||||
class RoleSeeder extends Seeder
|
||||
{
|
||||
@ -16,8 +14,51 @@ class RoleSeeder extends Seeder
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$adminRole = Role::create(['name' => 'Admin','guard_name' => 'web']);
|
||||
$salesRole = Role::create(['name' => 'Sales' ,'guard_name' => 'web']);
|
||||
$socialMedia = Role::create(['name' => 'Social Media Specialist','guard_name' => 'web']);
|
||||
app(PermissionRegistrar::class)->forgetCachedPermissions();
|
||||
|
||||
$permissions = [
|
||||
['name' => 'dashboard.view', 'group' => 'Dashboard', 'description' => 'View dashboard'],
|
||||
['name' => 'google-ads.accounts.view', 'group' => 'Google Ads Accounts', 'description' => 'View Google Ads accounts'],
|
||||
['name' => 'google-ads.accounts.sync', 'group' => 'Google Ads Accounts', 'description' => 'Sync Google account records'],
|
||||
['name' => 'google-ads.accounts.update', 'group' => 'Google Ads Accounts', 'description' => 'Update Google Ads account assignment details'],
|
||||
['name' => 'google-ads.activities.create', 'group' => 'Google Ads Activities', 'description' => 'Create account activities'],
|
||||
['name' => 'google-ads.activities.update', 'group' => 'Google Ads Activities', 'description' => 'Update account activities'],
|
||||
['name' => 'google-ads.activities.complete', 'group' => 'Google Ads Activities', 'description' => 'Mark account activities as complete'],
|
||||
['name' => 'google-ads.activities.delete', 'group' => 'Google Ads Activities', 'description' => 'Delete account activities'],
|
||||
['name' => 'google-ads.import', 'group' => 'Google Ads Accounts', 'description' => 'Import Google Ads data'],
|
||||
['name' => 'google.reports.view', 'group' => 'Google Reports', 'description' => 'View Google campaign reports'],
|
||||
['name' => 'client-invoices.create', 'group' => 'Client Invoices', 'description' => 'Create client invoices'],
|
||||
['name' => 'client-invoices.create-client', 'group' => 'Client Invoices', 'description' => 'Create clients from pending invoices'],
|
||||
['name' => 'client-invoices.update', 'group' => 'Client Invoices', 'description' => 'Update client invoices'],
|
||||
['name' => 'client-invoices.approve', 'group' => 'Client Invoices', 'description' => 'Approve client invoices'],
|
||||
['name' => 'client-invoices.delete', 'group' => 'Client Invoices', 'description' => 'Delete client invoices'],
|
||||
['name' => 'client-invoices.view-pdf', 'group' => 'Client Invoices', 'description' => 'View invoice PDF'],
|
||||
['name' => 'clients.adjustments.create', 'group' => 'Client Adjustments', 'description' => 'Create client invoice adjustments'],
|
||||
['name' => 'clients.adjustments.delete', 'group' => 'Client Adjustments', 'description' => 'Delete client invoice adjustments'],
|
||||
['name' => 'management.roles.view', 'group' => 'Role Management', 'description' => 'View roles'],
|
||||
['name' => 'management.roles.update', 'group' => 'Role Management', 'description' => 'Update role permissions'],
|
||||
['name' => 'management.users.view', 'group' => 'User Management', 'description' => 'View users'],
|
||||
['name' => 'management.users.create', 'group' => 'User Management', 'description' => 'Create users'],
|
||||
['name' => 'management.users.update', 'group' => 'User Management', 'description' => 'Update users'],
|
||||
];
|
||||
|
||||
foreach ($permissions as $permission) {
|
||||
Permission::query()->updateOrCreate(
|
||||
['name' => $permission['name'], 'guard_name' => 'web'],
|
||||
[
|
||||
'group' => $permission['group'],
|
||||
'group_name' => $permission['group'],
|
||||
'description' => $permission['description'],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
$adminRole = Role::query()->firstOrCreate(['name' => 'Admin', 'guard_name' => 'web']);
|
||||
Role::query()->firstOrCreate(['name' => 'Sales', 'guard_name' => 'web']);
|
||||
Role::query()->firstOrCreate(['name' => 'Social Media Specialist', 'guard_name' => 'web']);
|
||||
|
||||
$adminRole->syncPermissions(Permission::query()->pluck('name')->all());
|
||||
|
||||
app(PermissionRegistrar::class)->forgetCachedPermissions();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
|
||||
import { InertiaFormProps } from "@inertiajs/react";
|
||||
import { route } from "ziggy-js";
|
||||
import axios from "axios";
|
||||
import { Button, Stack, TextInput, NumberInput, Loader, Select, Switch, Text } from "@mantine/core";
|
||||
import { Button, Group, Stack, TextInput, NumberInput, Loader, Select, Switch, Text } from "@mantine/core";
|
||||
import { DateInput } from "@mantine/dates";
|
||||
import { IconDeviceFloppy } from "@tabler/icons-react";
|
||||
import dayjs from "dayjs";
|
||||
@ -17,11 +17,16 @@ export interface InvoiceFormValues {
|
||||
end_date: string;
|
||||
amount: string;
|
||||
management_fee: string;
|
||||
management_fee_amount?: string;
|
||||
management_fee_tax?: string;
|
||||
media_fee: string;
|
||||
media_fee_amount?: string;
|
||||
media_fee_tax?: string;
|
||||
tax_percent: string;
|
||||
total_spending: string;
|
||||
client_id?: string;
|
||||
customer_id?: string;
|
||||
nett_amount: string;
|
||||
}
|
||||
|
||||
interface InvoiceOption {
|
||||
@ -45,13 +50,30 @@ export default function InvoiceForm({
|
||||
const formatDate = (value: string) => (value ? dayjs(value).toDate() : null);
|
||||
const [fetchingSpend, setFetchingSpend] = useState(false);
|
||||
|
||||
const parseAmount = (value?: string) => {
|
||||
const parsed = Number.parseFloat(value ?? "");
|
||||
|
||||
return Number.isFinite(parsed) ? parsed : 0;
|
||||
};
|
||||
|
||||
const formatAmount = (value: number) => value.toFixed(2);
|
||||
|
||||
const calculateNettAmount = () => {
|
||||
const mediaFeeAmount = form.data.media_fee;
|
||||
const calculated = formatAmount(
|
||||
parseAmount(mediaFeeAmount) / (1 + parseAmount(form.data.tax_percent) / 100)
|
||||
);
|
||||
|
||||
form.setData("nett_amount", calculated);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!form.data.is_credit_card) return;
|
||||
|
||||
// Credit-card invoices can be fee-free; normalize fields to 0 for convenience.
|
||||
if (form.data.management_fee !== "0") form.setData("management_fee", "0");
|
||||
// if (form.data.management_fee !== "0") form.setData("management_fee", "0");
|
||||
if (form.data.media_fee !== "0") form.setData("media_fee", "0");
|
||||
if (form.data.tax_percent !== "0") form.setData("tax_percent", "0");
|
||||
// if (form.data.tax_percent !== "0") form.setData("tax_percent", "0");
|
||||
}, [form.data.is_credit_card]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -183,6 +205,21 @@ export default function InvoiceForm({
|
||||
required
|
||||
/>
|
||||
|
||||
<Group align="flex-end">
|
||||
<NumberInput
|
||||
precision={2}
|
||||
label="Media Nett Amount (RM)"
|
||||
value={form.data.nett_amount ? Number(form.data.nett_amount) : undefined}
|
||||
onChange={(value) => form.setData("nett_amount", value?.toString() ?? "")}
|
||||
error={form.errors.nett_amount}
|
||||
required
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<Button type="button" variant="outline" onClick={calculateNettAmount}>
|
||||
Calculate
|
||||
</Button>
|
||||
</Group>
|
||||
|
||||
<NumberInput
|
||||
precision={2}
|
||||
label="Total Spending (RM)"
|
||||
@ -192,17 +229,20 @@ export default function InvoiceForm({
|
||||
/>
|
||||
|
||||
<Text>Additional Info</Text>
|
||||
<Group spacing="sm">
|
||||
<Switch
|
||||
label="Credit card"
|
||||
aria-label="Credit card"
|
||||
checked={form.data.is_credit_card}
|
||||
onChange={(event) => form.setData("is_credit_card", event.currentTarget.checked)}
|
||||
/>
|
||||
<Text>Credit card</Text>
|
||||
</Group>
|
||||
|
||||
<Switch
|
||||
{/* <Switch
|
||||
label="Paid"
|
||||
checked={form.data.is_paid}
|
||||
onChange={(event) => form.setData("is_paid", event.currentTarget.checked)}
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
|
||||
@ -62,7 +62,7 @@ export default function UserForm({ role, status, permissions }: Props) {
|
||||
return (
|
||||
<Stack key={group} spacing="xs">
|
||||
<Checkbox
|
||||
label={<Text fw={600} sx={{ textTransform: 'capitalize' }}>{group}</Text>}
|
||||
label={<Text fw={600}>{group}</Text>}
|
||||
checked={allChecked}
|
||||
indeterminate={someChecked && !allChecked}
|
||||
onChange={(e) => handleGroupCheckboxChange(group, e.target.checked)}
|
||||
|
||||
@ -154,29 +154,47 @@ function AppNotifications() {
|
||||
accessorFn: (invoice) => invoice.pending_sql_acc_code ?? '-',
|
||||
Cell: ({ cell }) => <Text size="sm">{cell.getValue<string>()}</Text>,
|
||||
},
|
||||
{
|
||||
id: 'invoice_management_fee',
|
||||
header: 'Invoice Management Fee',
|
||||
accessorFn: (invoice) => invoice.invoice_billing_totals?.management_fee ?? 0,
|
||||
Cell: ({ row }) => (
|
||||
<Text size="sm" weight={600} align="right">
|
||||
{formatAmount(row.original.invoice_billing_totals?.management_fee ?? 0)}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'invoice_media_fee',
|
||||
header: 'Invoice Media Fee',
|
||||
accessorFn: (invoice) => invoice.invoice_billing_totals?.media_fee ?? 0,
|
||||
Cell: ({ row }) => (
|
||||
<Text size="sm" weight={600} align="right">
|
||||
{formatAmount(row.original.invoice_billing_totals?.media_fee ?? 0)}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
// {
|
||||
// id: 'invoice_management_fee',
|
||||
// header: 'Invoice Management Fee',
|
||||
// accessorFn: (invoice) => invoice.invoice_billing_totals?.management_fee ?? 0,
|
||||
// Cell: ({ row }) => (
|
||||
// <Text size="sm" weight={600} align="right">
|
||||
// {formatAmount(row.original.invoice_billing_totals?.management_fee ?? 0)}
|
||||
// </Text>
|
||||
// ),
|
||||
// },
|
||||
// {
|
||||
// id: 'invoice_media_fee',
|
||||
// header: 'Invoice Media Fee',
|
||||
// accessorFn: (invoice) => invoice.invoice_billing_totals?.media_fee ?? 0,
|
||||
// Cell: ({ row }) => (
|
||||
// <Text size="sm" weight={600} align="right">
|
||||
// {formatAmount(row.original.invoice_billing_totals?.media_fee ?? 0)}
|
||||
// </Text>
|
||||
// ),
|
||||
// },
|
||||
{
|
||||
accessorKey: 'management_fee',
|
||||
header: 'Payment Management Fee',
|
||||
header: 'Payment Management Fee (incl. tax)',
|
||||
Cell: ({ row }) => (
|
||||
<Text size="sm" weight={600} align="right">
|
||||
{formatAmount(row.original.management_fee_amount)}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'management_fee_tax',
|
||||
header: 'Payment Management Tax',
|
||||
Cell: ({ row }) => (
|
||||
<Text size="sm" weight={600} align="right">
|
||||
{formatAmount(row.original.management_fee_tax)}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'management_fee_nett',
|
||||
header: 'Payment Management Nett',
|
||||
Cell: ({ row }) => (
|
||||
<Text size="sm" weight={600} align="right">
|
||||
{formatAmount(row.original.management_fee)}
|
||||
@ -185,7 +203,25 @@ function AppNotifications() {
|
||||
},
|
||||
{
|
||||
accessorKey: 'media_fee',
|
||||
header: 'Payment Media Fee',
|
||||
header: 'Payment Media Fee (incl. tax)',
|
||||
Cell: ({ row }) => (
|
||||
<Text size="sm" weight={600} align="right">
|
||||
{formatAmount(row.original.media_fee_amount)}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'media_fee_tax',
|
||||
header: 'Payment Media Tax',
|
||||
Cell: ({ row }) => (
|
||||
<Text size="sm" weight={600} align="right">
|
||||
{formatAmount(row.original.media_fee_tax)}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'media_fee_nett',
|
||||
header: 'Payment Media Nett',
|
||||
Cell: ({ row }) => (
|
||||
<Text size="sm" weight={600} align="right">
|
||||
{formatAmount(row.original.media_fee)}
|
||||
@ -617,7 +653,7 @@ export default function AppLayout({ children }: Props) {
|
||||
</Group>
|
||||
|
||||
<Group spacing="sm">
|
||||
<AppNotifications />
|
||||
{/* <AppNotifications /> */}
|
||||
<ThemeToggle />
|
||||
<Menu shadow="md" width={220}>
|
||||
<Menu.Target>
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
ActionIcon,
|
||||
Stack,
|
||||
Text,
|
||||
Tabs,
|
||||
} from '@mantine/core';
|
||||
import { IconEye, IconRefresh } from '@tabler/icons-react';
|
||||
import { MantineReactTable } from 'mantine-react-table';
|
||||
@ -35,13 +36,60 @@ const parseAmount = (value?: number | string | null): number => {
|
||||
return Number.isNaN(normalized) ? 0 : normalized;
|
||||
};
|
||||
|
||||
const statusOrder = ['ENABLED', 'PAUSED', 'REMOVED', 'ENDED', 'CANCELED'];
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
if (status === 'ENABLED') {
|
||||
return 'green';
|
||||
}
|
||||
|
||||
if (status === 'PAUSED') {
|
||||
return 'yellow';
|
||||
}
|
||||
|
||||
if (status === 'ENDED' || status === 'CANCELED' || status === 'REMOVED') {
|
||||
return 'red';
|
||||
}
|
||||
|
||||
return 'gray';
|
||||
};
|
||||
|
||||
export default function TicketDetails({
|
||||
clients,
|
||||
googleCompanySyncRunning,
|
||||
}: Props) {
|
||||
const [syncing, setSyncing] = React.useState(false);
|
||||
// console.log(clients);
|
||||
const [activeStatus, setActiveStatus] = React.useState<string | null>('all');
|
||||
const campaignsData = clients ?? [];
|
||||
|
||||
const statusTabs = useMemo(() => {
|
||||
const counts = campaignsData.reduce<Record<string, number>>((acc, client) => {
|
||||
const status = client.status || 'UNKNOWN';
|
||||
acc[status] = (acc[status] ?? 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return Object.entries(counts).sort(([a], [b]) => {
|
||||
const aIndex = statusOrder.indexOf(a);
|
||||
const bIndex = statusOrder.indexOf(b);
|
||||
|
||||
if (aIndex !== -1 || bIndex !== -1) {
|
||||
return (aIndex === -1 ? Number.MAX_SAFE_INTEGER : aIndex)
|
||||
- (bIndex === -1 ? Number.MAX_SAFE_INTEGER : bIndex);
|
||||
}
|
||||
|
||||
return a.localeCompare(b);
|
||||
});
|
||||
}, [campaignsData]);
|
||||
|
||||
const filteredClients = useMemo(() => {
|
||||
if (!activeStatus || activeStatus === 'all') {
|
||||
return campaignsData;
|
||||
}
|
||||
|
||||
return campaignsData.filter((client) => (client.status || 'UNKNOWN') === activeStatus);
|
||||
}, [activeStatus, campaignsData]);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
@ -61,15 +109,7 @@ export default function TicketDetails({
|
||||
header: 'Status',
|
||||
Cell: ({ cell }: any) => {
|
||||
const value = cell.getValue() as string;
|
||||
const color =
|
||||
value === 'ENABLED'
|
||||
? 'green'
|
||||
: value === 'PAUSED'
|
||||
? 'yellow'
|
||||
: value === 'ENDED'
|
||||
? 'red'
|
||||
: 'gray';
|
||||
return <Badge color={color}>{value}</Badge>;
|
||||
return <Badge color={getStatusColor(value)}>{value}</Badge>;
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -142,10 +182,29 @@ export default function TicketDetails({
|
||||
</Group>
|
||||
<MantineReactTable
|
||||
columns={columns}
|
||||
data={clients}
|
||||
data={filteredClients}
|
||||
enableRowActions // ✅ REQUIRED
|
||||
positionActionsColumn="last" // optional but recommended
|
||||
renderRowActions={renderRowActions}
|
||||
renderTopToolbarCustomActions={() => (
|
||||
<Tabs value={activeStatus} onTabChange={setActiveStatus}>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value="all">All ({campaignsData.length})</Tabs.Tab>
|
||||
{statusTabs.map(([status, count]) => (
|
||||
<Tabs.Tab key={status} value={status}>
|
||||
<Group spacing={6}>
|
||||
<Badge color={getStatusColor(status)} variant="dot">
|
||||
{status}
|
||||
</Badge>
|
||||
<Text size="sm" color="dimmed">
|
||||
{count}
|
||||
</Text>
|
||||
</Group>
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs.List>
|
||||
</Tabs>
|
||||
)}
|
||||
/>
|
||||
</Container>
|
||||
</AppLayout>
|
||||
|
||||
@ -30,13 +30,24 @@ export default function Page({ clientId, customerId, availableInvoices }: Props)
|
||||
end_date: "",
|
||||
amount: "",
|
||||
management_fee: "",
|
||||
management_fee_amount: "",
|
||||
management_fee_tax: "",
|
||||
media_fee: "",
|
||||
media_fee_amount: "",
|
||||
media_fee_tax: "",
|
||||
tax_percent: "",
|
||||
total_spending: "",
|
||||
nett_amount: "",
|
||||
});
|
||||
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!form.data.nett_amount) {
|
||||
form.setError("nett_amount", "Media Nett Amount is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
const totalSpending = form.data.total_spending
|
||||
? parseFloat(form.data.total_spending)
|
||||
: null;
|
||||
@ -51,9 +62,14 @@ export default function Page({ clientId, customerId, availableInvoices }: Props)
|
||||
end_date: data.end_date || null,
|
||||
amount: parseFloat(data.amount) || 0,
|
||||
management_fee: parseFloat(data.management_fee) || 0,
|
||||
management_fee_amount: data.management_fee_amount ? parseFloat(data.management_fee_amount) : null,
|
||||
management_fee_tax: data.management_fee_tax ? parseFloat(data.management_fee_tax) : null,
|
||||
media_fee: parseFloat(data.media_fee) || 0,
|
||||
media_fee_amount: data.media_fee_amount ? parseFloat(data.media_fee_amount) : null,
|
||||
media_fee_tax: data.media_fee_tax ? parseFloat(data.media_fee_tax) : null,
|
||||
tax_percent: parseFloat(data.tax_percent) || 0,
|
||||
total_spending: totalSpending,
|
||||
nett_amount: parseFloat(form.data.nett_amount) || 0,
|
||||
}));
|
||||
|
||||
form.post(route("client-invoices.store"));
|
||||
|
||||
@ -30,10 +30,26 @@ export default function Page({ invoice, availableInvoices }: Props) {
|
||||
invoice.management_fee !== null && invoice.management_fee !== undefined
|
||||
? String(invoice.management_fee)
|
||||
: "",
|
||||
management_fee_amount:
|
||||
invoice.management_fee_amount !== null && invoice.management_fee_amount !== undefined
|
||||
? String(invoice.management_fee_amount)
|
||||
: "",
|
||||
management_fee_tax:
|
||||
invoice.management_fee_tax !== null && invoice.management_fee_tax !== undefined
|
||||
? String(invoice.management_fee_tax)
|
||||
: "",
|
||||
media_fee:
|
||||
invoice.media_fee !== null && invoice.media_fee !== undefined
|
||||
? String(invoice.media_fee)
|
||||
: "",
|
||||
media_fee_amount:
|
||||
invoice.media_fee_amount !== null && invoice.media_fee_amount !== undefined
|
||||
? String(invoice.media_fee_amount)
|
||||
: "",
|
||||
media_fee_tax:
|
||||
invoice.media_fee_tax !== null && invoice.media_fee_tax !== undefined
|
||||
? String(invoice.media_fee_tax)
|
||||
: "",
|
||||
tax_percent:
|
||||
invoice.tax_percent !== null && invoice.tax_percent !== undefined
|
||||
? String(invoice.tax_percent)
|
||||
@ -44,11 +60,18 @@ export default function Page({ invoice, availableInvoices }: Props) {
|
||||
: "",
|
||||
client_id: invoice.client_id ? String(invoice.client_id) : undefined,
|
||||
customer_id: invoice.client?.customer_id ?? "",
|
||||
nett_amount: invoice.nett_amount !== null && invoice.nett_amount !== undefined ? String(invoice.nett_amount) : "",
|
||||
});
|
||||
const isApproved = !!invoice.approved_at;
|
||||
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!form.data.nett_amount) {
|
||||
form.setError("nett_amount", "Media Nett Amount is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
const totalSpending = form.data.total_spending
|
||||
? parseFloat(form.data.total_spending)
|
||||
: null;
|
||||
@ -66,6 +89,7 @@ export default function Page({ invoice, availableInvoices }: Props) {
|
||||
media_fee: parseFloat(data.media_fee) || 0,
|
||||
tax_percent: parseFloat(data.tax_percent) || 0,
|
||||
total_spending: totalSpending,
|
||||
nett_amount: parseFloat(form.data.nett_amount) || 0,
|
||||
}));
|
||||
|
||||
form.put(route("client-invoices.update", { invoice: invoice.id }));
|
||||
@ -91,11 +115,11 @@ export default function Page({ invoice, availableInvoices }: Props) {
|
||||
</Badge>
|
||||
</Group>
|
||||
<Group gap="sm">
|
||||
{!isApproved && (
|
||||
{/* {!isApproved && (
|
||||
<Button color="green" onClick={handleApprove}>
|
||||
Approve invoice
|
||||
</Button>
|
||||
)}
|
||||
)} */}
|
||||
<Button
|
||||
component={Link}
|
||||
href={route("google-ads.accounts.show", { id: invoice.client?.customer_id ?? "" })}
|
||||
|
||||
@ -151,13 +151,13 @@ export default function Dashboard({
|
||||
icon: IconChecklist,
|
||||
color: 'violet',
|
||||
},
|
||||
{
|
||||
label: 'Missing SQL Code',
|
||||
value: stats.clientsMissingSqlCode.toLocaleString('en-MY'),
|
||||
detail: 'Synced clients not linked to SQL',
|
||||
icon: IconLinkOff,
|
||||
color: 'red',
|
||||
},
|
||||
// {
|
||||
// label: 'Missing SQL Code',
|
||||
// value: stats.clientsMissingSqlCode.toLocaleString('en-MY'),
|
||||
// detail: 'Synced clients not linked to SQL',
|
||||
// icon: IconLinkOff,
|
||||
// color: 'red',
|
||||
// },
|
||||
];
|
||||
|
||||
return (
|
||||
@ -191,7 +191,7 @@ export default function Dashboard({
|
||||
</Group>
|
||||
</Paper>
|
||||
|
||||
<SimpleGrid cols={4} breakpoints={[
|
||||
<SimpleGrid cols={3} breakpoints={[
|
||||
{ maxWidth: 'lg', cols: 2 },
|
||||
{ maxWidth: 'md', cols: 2 },
|
||||
{ maxWidth: 'xs', cols: 1 },
|
||||
|
||||
5
resources/js/types/index.d.ts
vendored
5
resources/js/types/index.d.ts
vendored
@ -3,6 +3,7 @@ import { LucideIcon } from 'lucide-react';
|
||||
|
||||
export interface Auth {
|
||||
user: User;
|
||||
permissions?: Record<string, boolean> | null;
|
||||
}
|
||||
|
||||
export interface BreadcrumbItem {
|
||||
@ -83,7 +84,11 @@ export interface ClientInvoice {
|
||||
amount: number;
|
||||
total_spend?: number | string;
|
||||
management_fee?: number;
|
||||
management_fee_amount?: number;
|
||||
management_fee_tax?: number;
|
||||
media_fee?: number;
|
||||
media_fee_amount?: number;
|
||||
media_fee_tax?: number;
|
||||
tax_percent?: number;
|
||||
nett_amount?: number;
|
||||
total_spending?: number | null;
|
||||
|
||||
@ -16,7 +16,9 @@
|
||||
})->name('home');
|
||||
|
||||
Route::middleware('auth')->group(function () {
|
||||
Route::get('dashboard', DashboardController::class)->name('dashboard');
|
||||
Route::get('dashboard', DashboardController::class)
|
||||
->middleware('permission:dashboard.view')
|
||||
->name('dashboard');
|
||||
|
||||
Route::prefix('google-ads')
|
||||
->name('google-ads.')
|
||||
@ -25,50 +27,65 @@
|
||||
->name('accounts.')
|
||||
->controller(GoogleAdsController::class)
|
||||
->group(function () {
|
||||
Route::get('/', 'accounts')->name('index');
|
||||
Route::get('/', 'accounts')
|
||||
->middleware('permission:google-ads.accounts.view')
|
||||
->name('index');
|
||||
Route::post('/sync-google-company-details', 'syncGoogleCompanyDetails')
|
||||
->middleware('permission:google-ads.accounts.sync')
|
||||
->name('sync-google-company-details');
|
||||
Route::get('/{id}/edit', 'edit')->name('edit');
|
||||
Route::get('/{id}', 'show')->name('show');
|
||||
Route::post('/{id}/account', 'updateAccount')->name('account.update');
|
||||
Route::get('/{id}/edit', 'edit')
|
||||
->middleware('permission:google-ads.accounts.update')
|
||||
->name('edit');
|
||||
Route::get('/{id}', 'show')
|
||||
->middleware('permission:google-ads.accounts.view')
|
||||
->name('show');
|
||||
Route::post('/{id}/account', 'updateAccount')
|
||||
->middleware('permission:google-ads.accounts.update')
|
||||
->name('account.update');
|
||||
Route::prefix('activity')
|
||||
->name('activity.')
|
||||
->controller(ActivityController::class)
|
||||
->group(function () {
|
||||
Route::post('/{id}/store', 'storeActivity')
|
||||
->middleware('permission:google-ads.activities.create')
|
||||
->name('storeActivity');
|
||||
Route::patch('/{id}/update', 'updateActivity')
|
||||
->middleware('permission:google-ads.activities.update')
|
||||
->name('updateActivity');
|
||||
Route::patch('/{id}/complete', 'completeActivity')
|
||||
->middleware('permission:google-ads.activities.complete')
|
||||
->name('completeActivity');
|
||||
Route::delete('/{id}/delete', 'deleteActivity')
|
||||
->middleware('permission:google-ads.activities.delete')
|
||||
->name('deleteActivity');
|
||||
});
|
||||
});
|
||||
Route::get('import', [GoogleAdsController::class, 'insertCSVDataToDB'])->name('import');
|
||||
Route::get('import', [GoogleAdsController::class, 'insertCSVDataToDB'])
|
||||
->middleware('permission:google-ads.import')
|
||||
->name('import');
|
||||
});
|
||||
|
||||
Route::prefix('client-invoices')
|
||||
->name('client-invoices.')
|
||||
->controller(ClientInvoiceController::class)
|
||||
->group(function () {
|
||||
Route::get('/create', 'create')->name('create');
|
||||
Route::post('/', 'store')->name('store');
|
||||
Route::get('{invoice}/client/create', 'createClient')->name('client.create');
|
||||
Route::post('{invoice}/client', 'storeClient')->name('client.store');
|
||||
Route::get('{invoice}/edit', 'edit')->name('edit');
|
||||
Route::put('{invoice}', 'update')->name('update');
|
||||
Route::patch('{invoice}/approve', 'approve')->name('approve');
|
||||
Route::delete('{invoice}', 'destroy')->name('destroy');
|
||||
Route::get('/pdf/invoice/{id}', 'getPdfInvoice')->name('getPdfInvoice');
|
||||
Route::get('/create', 'create')->middleware('permission:client-invoices.create')->name('create');
|
||||
Route::post('/', 'store')->middleware('permission:client-invoices.create')->name('store');
|
||||
Route::get('{invoice}/client/create', 'createClient')->middleware('permission:client-invoices.create-client')->name('client.create');
|
||||
Route::post('{invoice}/client', 'storeClient')->middleware('permission:client-invoices.create-client')->name('client.store');
|
||||
Route::get('{invoice}/edit', 'edit')->middleware('permission:client-invoices.update')->name('edit');
|
||||
Route::put('{invoice}', 'update')->middleware('permission:client-invoices.update')->name('update');
|
||||
Route::patch('{invoice}/approve', 'approve')->middleware('permission:client-invoices.approve')->name('approve');
|
||||
Route::delete('{invoice}', 'destroy')->middleware('permission:client-invoices.delete')->name('destroy');
|
||||
Route::get('/pdf/invoice/{id}', 'getPdfInvoice')->middleware('permission:client-invoices.view-pdf')->name('getPdfInvoice');
|
||||
});
|
||||
|
||||
Route::prefix('clients')
|
||||
->name('clients.')
|
||||
->controller(ClientInvoiceAdjustmentController::class)
|
||||
->group(function () {
|
||||
Route::post('{client}/adjustments', 'store')->name('adjustments.store');
|
||||
Route::delete('adjustments/{adjustment}', 'destroy')->name('adjustments.destroy');
|
||||
Route::post('{client}/adjustments', 'store')->middleware('permission:clients.adjustments.create')->name('adjustments.store');
|
||||
Route::delete('adjustments/{adjustment}', 'destroy')->middleware('permission:clients.adjustments.delete')->name('adjustments.destroy');
|
||||
});
|
||||
|
||||
Route::prefix('google')
|
||||
@ -76,6 +93,7 @@
|
||||
->controller(GoogleController::class)
|
||||
->group(function () {
|
||||
Route::post('/getCampaignsDetails', 'listCampaignsMetrics')
|
||||
->middleware('permission:google.reports.view')
|
||||
->name('getCampaignsDetails');
|
||||
});
|
||||
|
||||
@ -88,20 +106,20 @@
|
||||
->group(function () {
|
||||
// Route::post('/getCampaignsDetails', 'listCampaignsMetrics')
|
||||
// ->name('getCampaignsDetails');
|
||||
Route::get('/', 'index')->name('index');
|
||||
Route::get('{id}/edit/', 'edit')->name('edit');
|
||||
Route::post('{id}/update/', 'update')->name('update');
|
||||
Route::get('/', 'index')->middleware('permission:management.roles.view')->name('index');
|
||||
Route::get('{id}/edit/', 'edit')->middleware('permission:management.roles.update')->name('edit');
|
||||
Route::post('{id}/update/', 'update')->middleware('permission:management.roles.update')->name('update');
|
||||
});
|
||||
|
||||
Route::prefix('users')
|
||||
->name('users.')
|
||||
->controller(UserController::class)
|
||||
->group(function () {
|
||||
Route::get('/', 'index')->name('index');
|
||||
Route::get('/create', 'create')->name('create');
|
||||
Route::post('/store', 'store')->name('store');
|
||||
Route::get('{id}/edit', 'edit')->name('edit');
|
||||
Route::post('{id}/update', 'update')->name('update');
|
||||
Route::get('/', 'index')->middleware('permission:management.users.view')->name('index');
|
||||
Route::get('/create', 'create')->middleware('permission:management.users.create')->name('create');
|
||||
Route::post('/store', 'store')->middleware('permission:management.users.create')->name('store');
|
||||
Route::get('{id}/edit', 'edit')->middleware('permission:management.users.update')->name('edit');
|
||||
Route::post('{id}/update', 'update')->middleware('permission:management.users.update')->name('update');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user