| @@ -6,16 +6,18 @@ | ||
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | 8 | use FluentCart\App\Http\Controllers\AddonsController; |
| 9 | 9 | use FluentCart\App\Http\Controllers\AddressInfoController; |
| 10 | +use FluentCart\App\Http\Controllers\AttributesController; | |
| 10 | 11 | use FluentCart\App\Http\Controllers\AppControllers\AppController; |
| 11 | -use FluentCart\App\Http\Controllers\AttributesController; | |
| 12 | 12 | use FluentCart\App\Http\Controllers\CheckoutFieldsController; |
| 13 | 13 | use FluentCart\App\Http\Controllers\CustomerController; |
| 14 | 14 | use FluentCart\App\Http\Controllers\DashboardController; |
| 15 | +use FluentCart\App\Http\Controllers\DataBackfillController; | |
| 15 | 16 | use FluentCart\App\Http\Controllers\EmailNotificationController; |
| 16 | 17 | use FluentCart\App\Http\Controllers\FileUploadController; |
| 17 | 18 | use FluentCart\App\Http\Controllers\IntegrationController; |
| 19 | +use FluentCart\App\Http\Controllers\RenewalController; | |
| 18 | 20 | use FluentCart\App\Http\Controllers\LabelController; |
| 19 | 21 | use FluentCart\App\Http\Controllers\McpSettingsController; |
| 20 | 22 | use FluentCart\App\Http\Controllers\ModuleSettingsController; |
| 21 | 23 | use FluentCart\App\Http\Controllers\NotesController; |
| @@ -49,8 +51,12 @@ | ||
| 49 | 51 | $router->prefix('dashboard')->withPolicy('AdminPolicy')->group(function (Router $router) { |
| 50 | 52 | $router->get('/', [DashboardController::class, 'getOnboardingData']); |
| 51 | 53 | }); |
| 52 | 54 | |
| 55 | +$router->prefix('data-backfills')->withPolicy('AdminPolicy')->group(function (Router $router) { | |
| 56 | + $router->post('/run', [DataBackfillController::class, 'run']); | |
| 57 | +}); | |
| 58 | + | |
| 53 | 59 | $router->prefix('dashboard')->withPolicy('DashboardPolicy')->group(function (Router $router) { |
| 54 | 60 | $router->get('/stats', [DashboardController::class, 'getDashboardStats'])->meta([ |
| 55 | 61 | 'permissions' => 'dashboard_stats/view' |
| 56 | 62 | ]); |
| @@ -235,8 +241,21 @@ | ||
| 235 | 241 | $router->post('/variants', [ProductVariationController::class, 'create'])->meta([ |
| 236 | 242 | 'permissions' => 'products/create' |
| 237 | 243 | ]); |
| 238 | 244 | |
| 245 | + // Advanced Variation — bulk price/stock/status edit for variant combinations. | |
| 246 | + // Registered BEFORE the /variants/{variantId} routes so the first-match | |
| 247 | + // router doesn't capture 'bulk-update' as a {variantId}. | |
| 248 | + $router->post('/variants/bulk-update', [ProductVariationController::class, 'bulkUpdate'])->meta([ | |
| 249 | + 'permissions' => 'products/edit' | |
| 250 | + ]); | |
| 251 | + | |
| 252 | + // Group bulk update — PATCH semantics: only non-null fields are written. | |
| 253 | + // Registered BEFORE /variants/{variantId} to avoid the param capturing 'group-bulk-update'. | |
| 254 | + $router->post('/variants/group-bulk-update', [ProductVariationController::class, 'groupBulkUpdate'])->meta([ | |
| 255 | + 'permissions' => 'products/edit' | |
| 256 | + ]); | |
| 257 | + | |
| 239 | 258 | $router->post('/variants/{variantId}', [ProductVariationController::class, 'update'])->meta([ |
| 240 | 259 | 'permissions' => 'products/edit' |
| 241 | 260 | ]); |
| 242 | 261 | $router->post('/variants/{variantId}/tax-exempt', [ProductVariationController::class, 'updateTaxSettings'])->meta([ |
| @@ -261,8 +280,54 @@ | ||
| 261 | 280 | $router->get('/', [VariantController::class, 'index'])->meta([ |
| 262 | 281 | 'permissions' => 'products/view' |
| 263 | 282 | ]); |
| 264 | 283 | }); |
| 284 | + | |
| 285 | +// Advanced Variation — attribute library (groups + terms) | |
| 286 | +$router->prefix('options')->withPolicy('ProductPolicy')->group(function (Router $router) { | |
| 287 | + // Library endpoint — single payload powering the Advanced Variation library | |
| 288 | + // picker (full group list with terms + the frequently-used IDs for chips). | |
| 289 | + // Registered BEFORE 'attr/groups' to ensure the more-specific path wins. | |
| 290 | + $router->get('attr/groups/library', [AttributesController::class, 'getLibrary'])->meta([ | |
| 291 | + 'permissions' => 'products/view' | |
| 292 | + ]); | |
| 293 | + $router->get('attr/groups', [AttributesController::class, 'getGroups'])->meta([ | |
| 294 | + 'permissions' => 'products/view' | |
| 295 | + ]); | |
| 296 | + // Plural 'attr/groups/reorder' doesn't collide with the singular | |
| 297 | + // 'attr/group/{group_id}' routes below; kept here next to its list sibling. | |
| 298 | + $router->post('attr/groups/reorder', [AttributesController::class, 'reorderGroups'])->meta([ | |
| 299 | + 'permissions' => 'products/edit' | |
| 300 | + ]); | |
| 301 | + $router->post('attr/group/', [AttributesController::class, 'createGroup'])->meta([ | |
| 302 | + 'permissions' => 'products/create' | |
| 303 | + ]); | |
| 304 | + $router->get('attr/group/{group_id}', [AttributesController::class, 'getGroup'])->meta([ | |
| 305 | + 'permissions' => 'products/view' | |
| 306 | + ]); | |
| 307 | + $router->put('attr/group/{group_id}', [AttributesController::class, 'updateGroup'])->meta([ | |
| 308 | + 'permissions' => 'products/edit' | |
| 309 | + ]); | |
| 310 | + $router->delete('attr/group/{group_id}', [AttributesController::class, 'deleteGroup'])->meta([ | |
| 311 | + 'permissions' => 'products/delete' | |
| 312 | + ]); | |
| 313 | + $router->get('attr/group/{group_id}/terms', [AttributesController::class, 'getTerms'])->meta([ | |
| 314 | + 'permissions' => 'products/view' | |
| 315 | + ]); | |
| 316 | + $router->post('attr/group/{group_id}/terms', [AttributesController::class, 'createTerms'])->meta([ | |
| 317 | + 'permissions' => 'products/create' | |
| 318 | + ]); | |
| 319 | + $router->post('attr/group/{group_id}/term/{term_id}', [AttributesController::class, 'updateTerm'])->meta([ | |
| 320 | + 'permissions' => 'products/edit' | |
| 321 | + ]); | |
| 322 | + $router->delete('attr/group/{group_id}/term/{term_id}', [AttributesController::class, 'deleteTerm'])->meta([ | |
| 323 | + 'permissions' => 'products/delete' | |
| 324 | + ]); | |
| 325 | + $router->post('attr/group/{group_id}/terms/reorder', [AttributesController::class, 'reorderTerms'])->meta([ | |
| 326 | + 'permissions' => 'products/edit' | |
| 327 | + ]); | |
| 328 | +}); | |
| 329 | + | |
| 265 | 330 | //Integration route declaration |
| 266 | 331 | $router->prefix('integration')->withPolicy('IntegrationPolicy')->group(function (Router $router) { |
| 267 | 332 | |
| 268 | 333 | $router->get('addons', [AddonsController::class, 'getAddons'])->meta([ |
| @@ -488,9 +553,9 @@ | ||
| 488 | 553 | 'permissions' => 'orders/create' |
| 489 | 554 | ]); |
| 490 | 555 | |
| 491 | 556 | $router->post('/do-bulk-action', [OrderController::class, 'handleBulkActions'])->meta([ |
| 492 | - 'permissions' => 'orders/manage' | |
| 557 | + 'permissions' => 'orders/delete' // every accepted action deletes orders — same gate as DELETE /{order_id} | |
| 493 | 558 | ]); |
| 494 | 559 | |
| 495 | 560 | $router->post('/{order}/mark-as-paid', [OrderController::class, 'markAsPaid'])->meta([ |
| 496 | 561 | 'permissions' => 'orders/manage' |
| @@ -545,11 +610,13 @@ | ||
| 545 | 610 | $router->post('/{order}/transactions/{transaction_id}/accept-dispute/', [OrderController::class, 'acceptDispute'])->meta([ |
| 546 | 611 | 'permissions' => 'orders/manage' |
| 547 | 612 | ]); |
| 548 | 613 | |
| 549 | - $router->get('/{id}/transactions/{transaction_id}', [OrderController::class, 'getDetails'])->meta([ | |
| 550 | - 'permissions' => 'orders/view' | |
| 551 | - ]); | |
| 614 | + $router->get('/{id}/transactions/{transaction_id}', [OrderController::class, 'getTransactionDetails']) | |
| 615 | + ->int('id', 'transaction_id') | |
| 616 | + ->meta([ | |
| 617 | + 'permissions' => 'orders/view' | |
| 618 | + ]); | |
| 552 | 619 | |
| 553 | 620 | $router->put('/{order}/address/{id}', [OrderController::class, 'updateOrderAddress'])->meta([ |
| 554 | 621 | 'permissions' => 'orders/manage' |
| 555 | 622 | ]); |
| @@ -557,8 +624,12 @@ | ||
| 557 | 624 | $router->put('/{order}/transactions/{transaction}/status', [OrderController::class, 'updateTransactionStatus'])->meta([ |
| 558 | 625 | 'permissions' => 'orders/manage' |
| 559 | 626 | ]); |
| 560 | 627 | |
| 628 | + $router->post('/{order}/transactions/{transaction}/sync', [OrderController::class, 'syncPendingTransaction'])->meta([ | |
| 629 | + 'permissions' => 'orders/manage' | |
| 630 | + ]); | |
| 631 | + | |
| 561 | 632 | $router->post('/{order}/create-custom', [OrderController::class, 'createCustom'])->int('id')->meta([ |
| 562 | 633 | 'permissions' => 'orders/create' |
| 563 | 634 | ]); |
| 564 | 635 | |
| @@ -571,9 +642,26 @@ | ||
| 571 | 642 | ]); |
| 572 | 643 | |
| 573 | 644 | }); |
| 574 | 645 | |
| 646 | +$router->prefix('renewals')->withPolicy('OrderPolicy')->group(function (Router $router) { | |
| 647 | + $router->get('/', [RenewalController::class, 'index'])->meta([ | |
| 648 | + 'permissions' => 'orders/view' | |
| 649 | + ]); | |
| 575 | 650 | |
| 651 | + $router->get('/{id}', [RenewalController::class, 'show'])->int('id')->meta([ | |
| 652 | + 'permissions' => 'orders/view' | |
| 653 | + ]); | |
| 654 | + | |
| 655 | + $router->post('/{order}/void', [RenewalController::class, 'void'])->meta([ | |
| 656 | + 'permissions' => 'orders/manage' | |
| 657 | + ]); | |
| 658 | + | |
| 659 | + $router->post('/{order}/resend', [RenewalController::class, 'resend'])->meta([ | |
| 660 | + 'permissions' => 'orders/manage' | |
| 661 | + ]); | |
| 662 | +}); | |
| 663 | + | |
| 576 | 664 | $router->prefix('labels')->withPolicy('LabelPolicy')->group(function (Router $router) { |
| 577 | 665 | $router->get('/', [LabelController::class, 'index'])->meta([ |
| 578 | 666 | 'permissions' => 'labels/view' |
| 579 | 667 | ]); |
| @@ -660,51 +748,9 @@ | ||
| 660 | 748 | |
| 661 | 749 | }); |
| 662 | 750 | |
| 663 | 751 | |
| 664 | -$router->prefix('options')->withPolicy('ProductPolicy')->group(function (Router $router) { | |
| 665 | - $router->get('attr/groups', [AttributesController::class, 'getGroups'])->meta([ | |
| 666 | - 'permissions' => 'products/view' | |
| 667 | - ]); | |
| 668 | 752 | |
| 669 | - $router->post('attr/group/', [AttributesController::class, 'createGroup'])->meta([ | |
| 670 | - 'permissions' => 'products/create' | |
| 671 | - ]); | |
| 672 | - | |
| 673 | - $router->get('attr/group/{group_id}', [AttributesController::class, 'getGroup'])->meta([ | |
| 674 | - 'permissions' => 'products/view' | |
| 675 | - ]); | |
| 676 | - | |
| 677 | - $router->put('attr/group/{group_id}', [AttributesController::class, 'updateGroup'])->meta([ | |
| 678 | - 'permissions' => 'products/edit' | |
| 679 | - ]); | |
| 680 | - | |
| 681 | - $router->delete('attr/group/{group_id}', [AttributesController::class, 'deleteGroup'])->meta([ | |
| 682 | - 'permissions' => 'products/delete' | |
| 683 | - ]); | |
| 684 | - | |
| 685 | - $router->get('attr/group/{group_id}/terms', [AttributesController::class, 'getTerms'])->meta([ | |
| 686 | - 'permissions' => 'products/view' | |
| 687 | - ]); | |
| 688 | - | |
| 689 | - $router->post('attr/group/{group_id}/term', [AttributesController::class, 'createTerm'])->meta([ | |
| 690 | - 'permissions' => 'products/create' | |
| 691 | - ]); | |
| 692 | - | |
| 693 | - $router->post('attr/group/{group_id}/term/{term_id}', [AttributesController::class, 'updateTerm'])->meta([ | |
| 694 | - 'permissions' => 'products/edit' | |
| 695 | - ]); | |
| 696 | - | |
| 697 | - $router->delete('attr/group/{group_id}/term/{term_id}', [AttributesController::class, 'deleteTerm'])->meta([ | |
| 698 | - 'permissions' => 'products/delete' | |
| 699 | - ]); | |
| 700 | - | |
| 701 | - $router->post('attr/group/{group_id}/term/{term_id}/serial', [AttributesController::class, 'changeTermSerial'])->meta([ | |
| 702 | - 'permissions' => 'products/edit' | |
| 703 | - ]); | |
| 704 | -}); | |
| 705 | - | |
| 706 | - | |
| 707 | 753 | $router->prefix('onboarding')->withPolicy('AdminPolicy')->group(function (Router $router) { |
| 708 | 754 | $router->get('/', [OnboardingController::class, 'index']); |
| 709 | 755 | $router->post('/', [OnboardingController::class, 'saveSettings']); |
| 710 | 756 | $router->post('/create-pages', [OnboardingController::class, 'createPages']); |
| @@ -787,10 +833,10 @@ | ||
| 787 | 833 | }); |
| 788 | 834 | |
| 789 | 835 | $router->post('/upload-editor-file', [FileUploadController::class, 'uploadEditorFile'])->withPolicy('AdminPolicy'); |
| 790 | 836 | |
| 791 | -$router->prefix('notes')->withPolicy('AdminPolicy')->group(function (Router $router) { | |
| 792 | - $router->post('/attach', [NotesController::class, 'attach']); | |
| 837 | +$router->prefix('notes')->withPolicy('OrderPolicy')->group(function (Router $router) { | |
| 838 | + $router->post('/attach', [NotesController::class, 'attach'])->meta(['permissions' => 'orders/manage']); | |
| 793 | 839 | }); |
| 794 | 840 | |
| 795 | 841 | $router->prefix('app')->withPolicy('AdminPolicy')->group(function (Router $router) { |
| 796 | 842 | $router->get('init', [AppController::class, 'init']); |