From 2d057fac9caa8625d0e88aa74a79772a893ff49f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9D=99?= Date: Thu, 17 Mar 2022 06:50:46 +0000 Subject: [PATCH] =?UTF-8?q?!9=201.4.0=20*=20=E8=B0=83=E6=95=B4=E7=AD=BE?= =?UTF-8?q?=E7=BA=A6=E5=8F=91=E8=B4=A7=20*=20=E8=B0=83=E6=95=B4=E7=AD=BE?= =?UTF-8?q?=E7=BA=A6=E8=BF=9B=E8=B4=A7=E8=AE=BE=E7=BD=AE=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E5=BA=93=E5=AD=98=E9=97=AE=E9=A2=98=20*=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E5=95=86=E5=93=81=E5=AF=BC=E5=87=BA=20*=20?= =?UTF-8?q?=E3=80=90=E5=90=8E=E5=8F=B0=E3=80=91=E5=AF=BC=E5=87=BA=E5=95=86?= =?UTF-8?q?=E5=9F=8E=E8=AE=A2=E5=8D=95=E5=95=86=E5=93=81=20*=20Update=20*?= =?UTF-8?q?=20=E8=B0=83=E6=95=B4=E4=BA=91=E4=BB=93=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E4=B8=8D=E8=B6=B3=E4=BF=A1=E6=81=AF=E6=8F=90=E7=A4=BA=20*=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=92=A4=E9=94=80=E8=AE=A2=E5=8D=95=E5=8F=91?= =?UTF-8?q?=E8=B4=A7bug=20*=20=E8=B0=83=E8=AF=95=E5=8F=96=E6=B6=88?= =?UTF-8?q?=E6=8F=90=E8=B4=A7=E5=8D=95=E6=92=A4=E5=9B=9E=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E5=B7=B2=E5=8F=91=E7=9A=84=E6=9C=AC=E5=9C=B0=E8=B4=A7=20*=20?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98JSAPI=20*=20=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E5=8F=96=E6=B6=88=E6=94=AF=E4=BB=98=20*=20=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E6=92=A4=E9=94=80=E6=94=AF=E4=BB=98=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=97=A5=E5=BF=97=E6=8E=92=E5=BA=8F=20*=20?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=AD=BE=E7=BA=A6=20=E4=BA=91=E4=BB=93?= =?UTF-8?q?=E5=8F=91=E8=B4=A7=20*=20=E8=B0=83=E6=95=B4=E7=AD=BE=E7=BA=A6?= =?UTF-8?q?=E5=8F=91=E8=B4=A7=E8=AE=A2=E5=8D=95=E5=BA=93=E5=AD=98=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20*=20WIP=20*=20=E8=B0=83=E6=95=B4=E7=BB=8F=E9=94=80?= =?UTF-8?q?=E5=95=86=E5=8F=91=E8=B4=A7=E6=8A=A5=E9=94=99=20*=20=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E7=AD=BE=E7=BA=A6=E5=8F=91=E8=B4=A7=20*=20=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E7=AD=BE=E7=BA=A6=E5=8F=91=E8=B4=A7=E5=86=85=E5=AE=B9?= =?UTF-8?q?=20*=20=E6=B7=BB=E5=8A=A0=E7=AD=BE=E7=BA=A6=E4=BA=91=E4=BB=93?= =?UTF-8?q?=E5=8F=91=E8=B4=A7=20*=20Fix=20*=20=E3=80=90=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E3=80=91=E7=AD=BE=E7=BA=A6=E6=B8=A0=E9=81=93=E8=A1=A5=E8=B4=B4?= =?UTF-8?q?=20*=20=E3=80=90=E5=90=8E=E5=8F=B0=E3=80=91=E8=BF=9B=E8=B4=A7?= =?UTF-8?q?=E8=A1=A5=E8=B4=B4=E4=BC=98=E5=8C=96=20*=20Update=20*=20?= =?UTF-8?q?=E7=BB=8F=E9=94=80=E5=95=86=E5=95=86=E5=93=81=E5=8F=98=E6=9B=B4?= =?UTF-8?q?=E6=95=B0=E9=87=8F=E6=A0=BC=E5=BC=8F=E5=8C=96=20*=20=E3=80=90?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E3=80=91=E4=BC=98=E5=8C=96=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E8=80=85=E6=B4=A5=E8=B4=B4=E5=92=8C=E7=AE=A1=E7=90=86=E6=B4=A5?= =?UTF-8?q?=E8=B4=B4=20*=20=E3=80=90=E5=90=8E=E5=8F=B0=E3=80=91=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=94=AF=E4=BB=98=E7=8A=B6=E6=80=81=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=20*=20=E3=80=90=E5=90=8E=E5=8F=B0=E3=80=91=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E6=B4=A5=E8=B4=B4=E7=8A=B6=E6=80=81=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=20*=20=E3=80=90=E5=90=8E=E5=8F=B0=E3=80=91=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=AE=A1=E7=90=86=E8=80=85=E6=B4=A5=E8=B4=B4=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=98=BE=E7=A4=BA=20*=20=E6=8F=90=E8=B4=A7=E5=8D=95?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=9B=9E=E8=B0=83=E5=A4=84=E7=90=86=20*=20?= =?UTF-8?q?=E6=89=B9=E9=9B=B6=E5=95=86=E5=93=81=E5=8F=98=E6=9B=B4=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=20*=20=E3=80=90=E5=90=8E=E5=8F=B0=E3=80=91=E4=BA=91?= =?UTF-8?q?=E4=BB=93=E6=8F=90=E8=B4=A7=E5=8D=95=20*=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=9C=86=E7=82=B9=E6=89=A9=E5=B1=95=20*=20=E4=BA=91=E4=BB=93?= =?UTF-8?q?=E8=BF=90=E8=B4=B9=E9=85=8D=E7=BD=AE=20*=20=E5=8F=96=E6=B6=88?= =?UTF-8?q?=E6=8F=90=E8=B4=A7=E5=8D=95=20*=20=E6=9B=B4=E6=96=B0=E8=BF=9B?= =?UTF-8?q?=E8=B4=A7=E4=B8=9A=E7=BB=A9=E7=BC=93=E5=AD=98=E6=9C=89=E6=95=88?= =?UTF-8?q?=E6=9C=9F=20*=20=E4=BF=AE=E6=94=B9=E6=94=AF=E4=BB=98=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E4=BA=91=E4=BB=93=E5=BA=93=E5=8F=91=E8=B4=A7=20*=20WI?= =?UTF-8?q?P=20*=20=E6=8F=90=E8=B4=A7=E5=8D=95=E5=88=97=E8=A1=A8=E5=92=8C?= =?UTF-8?q?=E8=AF=A6=E6=83=85=20*=20=E5=88=9B=E5=BB=BA=E6=8F=90=E8=B4=A7?= =?UTF-8?q?=E5=8D=95,=E6=8F=90=E8=B4=A7=E5=8D=95=E4=BB=98=E6=AC=BE=20*=20?= =?UTF-8?q?=E5=A4=84=E7=90=86=E7=BA=BF=E4=B8=8B=E4=BB=98=E6=AC=BE=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=BA=93=E5=AD=98=20*=20=E4=BF=AE=E6=94=B9=E7=BB=8F?= =?UTF-8?q?=E9=94=80=E5=95=86=E9=85=8D=E7=BD=AE=E8=BF=94=E5=9B=9E=E6=89=8B?= =?UTF-8?q?=E7=BB=AD=E8=B4=B9=E7=8E=87=20*=20=E6=B7=BB=E5=8A=A0=E7=BB=8F?= =?UTF-8?q?=E9=94=80=E5=95=86=E9=85=8D=E7=BD=AE=E8=BF=94=E5=9B=9E=E6=89=8B?= =?UTF-8?q?=E7=BB=AD=E8=B4=B9=E7=8E=87=20*=20=E5=AE=8C=E5=96=84=E8=BF=9B?= =?UTF-8?q?=E8=B4=A7=E4=BA=91=E5=BA=93=E5=AD=98=20*=20=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=90=88=E5=B9=B6=20*=20=E6=B7=BB=E5=8A=A0=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=89=93=E6=AC=BE=E8=A1=A5=E8=B4=B4=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E4=BB=98=E6=96=B9=E5=BC=8F=20*=20=E5=8F=96?= =?UTF-8?q?=E6=B6=88=E6=89=B9=E9=9B=B6=E6=8F=90=E7=8E=B0=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E9=97=B4=E9=9A=94=E9=99=90=E5=88=B6=20*=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E9=85=8D=E7=BD=AE=E7=AE=A1=E7=90=86?= =?UTF-8?q?=20*=20=E7=94=A8=E6=88=B7=E5=95=86=E5=93=81=E6=89=98=E7=AE=A1?= =?UTF-8?q?=E5=BA=93=E5=AD=98=20*=20=E6=96=B0=E5=A2=9E=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=95=86=E5=93=81=E6=89=98=E7=AE=A1=E5=BA=93=E5=AD=98=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Grid/DealerChannelSubsidyBatchPay.php | 53 ++++ .../Actions/Grid/DealerChannelSubsidyPay.php | 44 +++ .../Grid/DealerManageSubsidyBatchPay.php | 6 +- .../Actions/Grid/DealerManageSubsidyPay.php | 8 +- .../Grid/DealerManagerSubsidyBatchPay.php | 6 +- .../Actions/Grid/DealerManagerSubsidyPay.php | 8 +- .../Grid/DealerPurchaseSubsidyBatchPay.php | 53 ++++ .../Actions/Grid/DealerPurchaseSubsidyPay.php | 46 +++ .../Actions/Grid/Exports/ShippingOrder.php | 2 + app/Admin/Actions/Show/DealerEarningPay.php | 1 + .../DealerChannelSubsidyController.php | 141 ++++++++++ app/Admin/Controllers/DealerController.php | 3 +- .../DealerDeliveryBillController.php | 134 +++++++++ .../DealerManageSubsidyController.php | 44 +-- .../DealerManagerSubsidyController.php | 4 +- .../Controllers/DealerOrderController.php | 38 +-- .../DealerPurchaseSubsidyController.php | 186 +++++++++++++ app/Admin/Controllers/OrderController.php | 261 +++++++++++------- app/Admin/Controllers/SettingController.php | 20 ++ app/Admin/Extensions/Column/CircleDot.php | 50 ++++ .../Grid/Tools/Order/ExportProduct.php | 43 +++ app/Admin/Forms/DealerEarningPay.php | 3 +- app/Admin/Forms/Settings/Custom.php | 48 ++++ app/Admin/Forms/Settings/Dealer.php | 2 + .../DealerUserProductLogSimpleTable.php | 11 +- app/Admin/Repositories/DealerDeliveryBill.php | 16 ++ .../Repositories/DealerDeliveryProduct.php | 16 ++ .../Repositories/DealerPurchaseSubsidy.php | 16 ++ .../Repositories/DealerPurchaseSubsidyLog.php | 16 ++ app/Admin/Services/DealerEarningService.php | 111 ++++++++ app/Admin/bootstrap.php | 24 ++ app/Admin/routes.php | 9 + app/Constants/OrderStatus.php | 10 + .../Api/Filters/DealerDeliveryBillFilter.php | 26 ++ .../Dealer/ConfigurationController.php | 2 + .../Dealer/DealerDeliveryBillController.php | 178 ++++++++++++ .../Controllers/Dealer/OrderController.php | 67 ++++- .../Dealer/UserProductController.php | 2 +- .../Http/Controllers/SettingController.php | 13 + .../Dealer/DealerDeliveryBillResource.php | 34 +++ .../Dealer/DealerDeliveryProductResource.php | 24 ++ .../Resources/Dealer/OrderProductResource.php | 2 + .../Dealer/UserProductLogResource.php | 8 +- .../Resources/Dealer/UserProductResource.php | 1 + app/Endpoint/Api/routes.php | 8 + app/Enums/DealerDeliveryBillStatus.php | 32 +++ app/Enums/DealerEarningStatus.php | 32 ++- app/Enums/DealerManageSubsidyStatus.php | 16 +- app/Enums/DealerManagerSubsidyStatus.php | 8 + app/Enums/DealerPurchaseSubsidyStatus.php | 21 ++ app/Enums/DealerWalletAction.php | 1 + app/Enums/PayWay.php | 37 ++- app/Models/Dealer.php | 5 +- app/Models/DealerDeliveryBill.php | 79 ++++++ app/Models/DealerDeliveryProduct.php | 13 + app/Models/DealerEarning.php | 32 +++ app/Models/DealerOrder.php | 8 + app/Models/DealerOrderProduct.php | 1 + app/Models/DealerPurchaseSubsidy.php | 19 +- app/Models/DealerUserProduct.php | 6 + app/Models/DealerUserProductLog.php | 38 ++- app/Models/Order.php | 10 + app/Models/PayLog.php | 14 + app/Models/User.php | 9 + app/Providers/AppServiceProvider.php | 1 + .../Dealer/DealerDeliveryBillService.php | 259 +++++++++++++++++ app/Services/Dealer/ManageSubsidyService.php | 43 --- app/Services/Dealer/ManagerSubsidyService.php | 43 --- app/Services/Dealer/OrderService.php | 231 +++++++++++++--- app/Services/PayService.php | 52 ++++ ...it_stock_to_dealer_user_products_table.php | 32 +++ ...sit_qty_to_dealer_order_products_table.php | 32 +++ ...741_create_dealer_delivery_bills_table.php | 50 ++++ ..._create_dealer_delivery_products_table.php | 36 +++ ...osit_to_dealer_user_product_logs_table.php | 34 +++ ..._deposit_status_to_dealer_orders_table.php | 35 +++ database/seeders/AdminMenuSeeder.php | 13 + database/seeders/AdminPermissionSeeder.php | 22 ++ 78 files changed, 2705 insertions(+), 357 deletions(-) create mode 100644 app/Admin/Actions/Grid/DealerChannelSubsidyBatchPay.php create mode 100644 app/Admin/Actions/Grid/DealerChannelSubsidyPay.php create mode 100644 app/Admin/Actions/Grid/DealerPurchaseSubsidyBatchPay.php create mode 100644 app/Admin/Actions/Grid/DealerPurchaseSubsidyPay.php create mode 100644 app/Admin/Controllers/DealerChannelSubsidyController.php create mode 100644 app/Admin/Controllers/DealerDeliveryBillController.php create mode 100644 app/Admin/Controllers/DealerPurchaseSubsidyController.php create mode 100644 app/Admin/Extensions/Column/CircleDot.php create mode 100644 app/Admin/Extensions/Grid/Tools/Order/ExportProduct.php create mode 100644 app/Admin/Forms/Settings/Custom.php create mode 100644 app/Admin/Repositories/DealerDeliveryBill.php create mode 100644 app/Admin/Repositories/DealerDeliveryProduct.php create mode 100644 app/Admin/Repositories/DealerPurchaseSubsidy.php create mode 100644 app/Admin/Repositories/DealerPurchaseSubsidyLog.php create mode 100644 app/Admin/Services/DealerEarningService.php create mode 100644 app/Endpoint/Api/Filters/DealerDeliveryBillFilter.php create mode 100644 app/Endpoint/Api/Http/Controllers/Dealer/DealerDeliveryBillController.php create mode 100644 app/Endpoint/Api/Http/Resources/Dealer/DealerDeliveryBillResource.php create mode 100644 app/Endpoint/Api/Http/Resources/Dealer/DealerDeliveryProductResource.php create mode 100644 app/Enums/DealerDeliveryBillStatus.php create mode 100644 app/Models/DealerDeliveryBill.php create mode 100644 app/Models/DealerDeliveryProduct.php create mode 100644 app/Services/Dealer/DealerDeliveryBillService.php delete mode 100644 app/Services/Dealer/ManageSubsidyService.php delete mode 100644 app/Services/Dealer/ManagerSubsidyService.php create mode 100644 database/migrations/2022_03_08_100618_add_deposit_stock_to_dealer_user_products_table.php create mode 100644 database/migrations/2022_03_08_103158_add_deposit_qty_to_dealer_order_products_table.php create mode 100644 database/migrations/2022_03_08_105741_create_dealer_delivery_bills_table.php create mode 100644 database/migrations/2022_03_08_105753_create_dealer_delivery_products_table.php create mode 100644 database/migrations/2022_03_08_145732_add_is_deposit_to_dealer_user_product_logs_table.php create mode 100644 database/migrations/2022_03_14_113838_add_deposit_status_to_dealer_orders_table.php diff --git a/app/Admin/Actions/Grid/DealerChannelSubsidyBatchPay.php b/app/Admin/Actions/Grid/DealerChannelSubsidyBatchPay.php new file mode 100644 index 00000000..372217f9 --- /dev/null +++ b/app/Admin/Actions/Grid/DealerChannelSubsidyBatchPay.php @@ -0,0 +1,53 @@ + 付款'; + + /** + * @param Model|Authenticatable|HasPermissions|null $user + * + * @return bool + */ + protected function authorize($user): bool + { + return $user->can('dcat.admin.dealer_channel_subsidies.batch_pay'); + } + + // 确认弹窗信息 + public function confirm() + { + return '您确定要支付选中的渠道补贴吗?'; + } + + // 处理请求 + public function handle(Request $request) + { + try { + DB::transaction(function () { + foreach ($this->getKey() as $id) { + $dealerEarning = DealerEarning::lockForUpdate()->channelSubsidy()->withoutPayer()->find($id); + + if (! $dealerEarning?->isSettled() || ! $dealerEarning?->isPending()) { + continue; + } + + (new DealerEarningService())->pay($dealerEarning); + } + }); + } catch (Throwable $e) { + return $this->response()->error('操作失败:'.$e->getMessage())->refresh(); + } + + return $this->response()->success('操作成功')->refresh(); + } +} diff --git a/app/Admin/Actions/Grid/DealerChannelSubsidyPay.php b/app/Admin/Actions/Grid/DealerChannelSubsidyPay.php new file mode 100644 index 00000000..4566557f --- /dev/null +++ b/app/Admin/Actions/Grid/DealerChannelSubsidyPay.php @@ -0,0 +1,44 @@ + 付款'; + + /** + * @param Model|Authenticatable|HasPermissions|null $user + * + * @return bool + */ + protected function authorize($user): bool + { + return $user->can('dcat.admin.dealer_channel_subsidies.pay'); + } + + // 确认弹窗信息 + public function confirm() + { + return '您确定要支付选中的渠道补贴吗?'; + } + + // 处理请求 + public function handle(Request $request) + { + DB::transaction(function () { + $id = $this->getKey(); + + $dealerEarning = DealerEarning::lockForUpdate()->channelSubsidy()->withoutPayer()->find($id); + + (new DealerEarningService())->pay($dealerEarning); + }); + + return $this->response()->success('操作成功')->refresh(); + } +} diff --git a/app/Admin/Actions/Grid/DealerManageSubsidyBatchPay.php b/app/Admin/Actions/Grid/DealerManageSubsidyBatchPay.php index caa4546d..de1253db 100644 --- a/app/Admin/Actions/Grid/DealerManageSubsidyBatchPay.php +++ b/app/Admin/Actions/Grid/DealerManageSubsidyBatchPay.php @@ -2,8 +2,8 @@ namespace App\Admin\Actions\Grid; +use App\Admin\Services\DealerEarningService; use App\Models\DealerManageSubsidy; -use App\Services\Dealer\ManageSubsidyService; use Dcat\Admin\Grid\BatchAction; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -38,7 +38,9 @@ class DealerManageSubsidyBatchPay extends BatchAction $dealerManageSubsidy = DealerManageSubsidy::lockForUpdate()->settled()->find($id); if ($dealerManageSubsidy?->isPending()) { - (new ManageSubsidyService())->pay($dealerManageSubsidy); + (new DealerEarningService())->pay( + $dealerManageSubsidy->earning->setRelation('earningable', $dealerManageSubsidy) + ); } } }); diff --git a/app/Admin/Actions/Grid/DealerManageSubsidyPay.php b/app/Admin/Actions/Grid/DealerManageSubsidyPay.php index 6f21ea8d..b4300891 100644 --- a/app/Admin/Actions/Grid/DealerManageSubsidyPay.php +++ b/app/Admin/Actions/Grid/DealerManageSubsidyPay.php @@ -2,8 +2,8 @@ namespace App\Admin\Actions\Grid; +use App\Admin\Services\DealerEarningService; use App\Models\DealerManageSubsidy; -use App\Services\Dealer\ManageSubsidyService; use Dcat\Admin\Grid\RowAction; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -34,8 +34,10 @@ class DealerManageSubsidyPay extends RowAction DB::transaction(function () { $id = $this->getKey(); - (new ManageSubsidyService())->pay( - DealerManageSubsidy::lockForUpdate()->settled()->findOrFail($id) + $dealerManageSubsidy = DealerManageSubsidy::lockForUpdate()->settled()->findOrFail($id); + + (new DealerEarningService())->pay( + $dealerManageSubsidy->earning->setRelation('earningable', $dealerManageSubsidy) ); }); diff --git a/app/Admin/Actions/Grid/DealerManagerSubsidyBatchPay.php b/app/Admin/Actions/Grid/DealerManagerSubsidyBatchPay.php index 39365184..10457f03 100644 --- a/app/Admin/Actions/Grid/DealerManagerSubsidyBatchPay.php +++ b/app/Admin/Actions/Grid/DealerManagerSubsidyBatchPay.php @@ -2,8 +2,8 @@ namespace App\Admin\Actions\Grid; +use App\Admin\Services\DealerEarningService; use App\Models\DealerManagerSubsidy; -use App\Services\Dealer\ManagerSubsidyService; use Dcat\Admin\Grid\BatchAction; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -38,7 +38,9 @@ class DealerManagerSubsidyBatchPay extends BatchAction $dealerManagerSubsidy = DealerManagerSubsidy::lockForUpdate()->settled()->find($id); if ($dealerManagerSubsidy?->isPending()) { - (new ManagerSubsidyService())->pay($dealerManagerSubsidy); + (new DealerEarningService())->pay( + $dealerManagerSubsidy->earning->setRelation('earningable', $dealerManagerSubsidy) + ); } } }); diff --git a/app/Admin/Actions/Grid/DealerManagerSubsidyPay.php b/app/Admin/Actions/Grid/DealerManagerSubsidyPay.php index 0c706e8c..b0dfb63b 100644 --- a/app/Admin/Actions/Grid/DealerManagerSubsidyPay.php +++ b/app/Admin/Actions/Grid/DealerManagerSubsidyPay.php @@ -2,8 +2,8 @@ namespace App\Admin\Actions\Grid; +use App\Admin\Services\DealerEarningService; use App\Models\DealerManagerSubsidy; -use App\Services\Dealer\ManagerSubsidyService; use Dcat\Admin\Grid\RowAction; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -34,8 +34,10 @@ class DealerManagerSubsidyPay extends RowAction DB::transaction(function () { $id = $this->getKey(); - (new ManagerSubsidyService())->pay( - DealerManagerSubsidy::lockForUpdate()->settled()->findOrFail($id) + $dealerManagerSubsidy = DealerManagerSubsidy::lockForUpdate()->settled()->findOrFail($id); + + (new DealerEarningService())->pay( + $dealerManagerSubsidy->earning->setRelation('earningable', $dealerManagerSubsidy) ); }); diff --git a/app/Admin/Actions/Grid/DealerPurchaseSubsidyBatchPay.php b/app/Admin/Actions/Grid/DealerPurchaseSubsidyBatchPay.php new file mode 100644 index 00000000..59d95980 --- /dev/null +++ b/app/Admin/Actions/Grid/DealerPurchaseSubsidyBatchPay.php @@ -0,0 +1,53 @@ + 付款'; + + /** + * @param Model|Authenticatable|HasPermissions|null $user + * + * @return bool + */ + protected function authorize($user): bool + { + return $user->can('dcat.admin.dealer_purchase_subsidies.batch_pay'); + } + + // 确认弹窗信息 + public function confirm() + { + return '您确定要支付选中的进货补贴吗?'; + } + + // 处理请求 + public function handle(Request $request) + { + try { + DB::transaction(function () { + foreach ($this->getKey() as $id) { + $dealerPurchaseSubsidy = DealerPurchaseSubsidy::lockForUpdate()->settleCompleted()->find($id); + + if ($dealerPurchaseSubsidy?->isPending()) { + (new DealerEarningService())->pay( + $dealerPurchaseSubsidy->earning->setRelation('earningable', $dealerPurchaseSubsidy) + ); + } + } + }); + } catch (Throwable $e) { + return $this->response()->error('操作失败:'.$e->getMessage())->refresh(); + } + + return $this->response()->success('操作成功')->refresh(); + } +} diff --git a/app/Admin/Actions/Grid/DealerPurchaseSubsidyPay.php b/app/Admin/Actions/Grid/DealerPurchaseSubsidyPay.php new file mode 100644 index 00000000..5f3f714b --- /dev/null +++ b/app/Admin/Actions/Grid/DealerPurchaseSubsidyPay.php @@ -0,0 +1,46 @@ + 付款'; + + /** + * @param Model|Authenticatable|HasPermissions|null $user + * + * @return bool + */ + protected function authorize($user): bool + { + return $user->can('dcat.admin.dealer_purchase_subsidies.pay'); + } + + // 确认弹窗信息 + public function confirm() + { + return '您确定要支付选中的进货补贴吗?'; + } + + // 处理请求 + public function handle(Request $request) + { + DB::transaction(function () { + $id = $this->getKey(); + + $dealerPurchaseSubsidy = DealerPurchaseSubsidy::lockForUpdate()->settleCompleted()->findOrFail($id); + + (new DealerEarningService())->pay( + $dealerPurchaseSubsidy->earning->setRelation('earningable', $dealerPurchaseSubsidy) + ); + }); + + return $this->response()->success('操作成功')->refresh(); + } +} diff --git a/app/Admin/Actions/Grid/Exports/ShippingOrder.php b/app/Admin/Actions/Grid/Exports/ShippingOrder.php index c936d863..aaf94628 100644 --- a/app/Admin/Actions/Grid/Exports/ShippingOrder.php +++ b/app/Admin/Actions/Grid/Exports/ShippingOrder.php @@ -12,6 +12,8 @@ use Illuminate\Http\Request; class ShippingOrder extends RowAction { + protected $htmlClasses = ['btn', 'btn-primary']; + public function title() { if ($this->title) { diff --git a/app/Admin/Actions/Show/DealerEarningPay.php b/app/Admin/Actions/Show/DealerEarningPay.php index 804e08e7..6ab63a0b 100644 --- a/app/Admin/Actions/Show/DealerEarningPay.php +++ b/app/Admin/Actions/Show/DealerEarningPay.php @@ -49,6 +49,7 @@ class DealerEarningPay extends AbstractTool DB::beginTransaction(); $earning = DealerEarning::findOrFail($key); $earning->update([ + 'pay_way' => DealerEarning::PAY_WAY_WALLET, 'pay_info' => $earning->getPayInfo(), 'pay_at' => now(), 'status' => DealerEarningStatus::Completed, diff --git a/app/Admin/Controllers/DealerChannelSubsidyController.php b/app/Admin/Controllers/DealerChannelSubsidyController.php new file mode 100644 index 00000000..75efaafa --- /dev/null +++ b/app/Admin/Controllers/DealerChannelSubsidyController.php @@ -0,0 +1,141 @@ +model()->channelSubsidy()->withoutPayer()->orderBy('id', 'desc'); + + $grid->column('id')->sortable(); + $grid->column('user.phone', '手机号'); + $grid->column('user.userInfo.nickname', '昵称'); + $grid->column('lvl', '经销商等级')->display(function () { + return $this->lvl->text(); + }); + $grid->column('total_amount', '补贴金额')->prepend('¥'); + $grid->column('fee', '手续费')->prepend('¥')->help('手续费=补贴金额*手续费率'); + $grid->column('fee_rate', '手续费率')->append('%'); + $grid->column('total_earnings', '总收入')->prepend('¥')->help('总收入=补贴金额-手续费'); + $grid->column('remark', '备注')->display('查看')->modal(function ($modal) { + $modal->title('备注'); + return '
'.nl2br($this->remark).'
'; + }); + $grid->column('settle_at', '结算时间')->display(function () { + return $this->settle_at?->toDateTimeString(); + })->sortable(); + $grid->column('status', '状态')->display(function ($v) { + if (! $this->isSettled()) { + return "  待结算"; + } + + return "  {$v->text()}"; + }); + $grid->column('pay_at', '付款时间')->display(function () { + return $this->pay_at?->toDateTimeString(); + })->sortable(); + $grid->column('created_at', '创建时间')->display(function () { + return $this->created_at?->toDateTimeString(); + }); + + $grid->showRowSelector(); + + $grid->tools(function ($tools) { + $tools->batch(function ($batch) { + $batch->disableDelete(); + + if (Admin::user()->can('dcat.admin.dealer_channel_subsidies.batch_pay')) { + $batch->add(new DealerChannelSubsidyBatchPay()); + } + }); + }); + + $grid->actions(function (Grid\Displayers\Actions $actions) { + if ( + $actions->row->isSettled() && + $actions->row->isPending() && + Admin::user()->can('dcat.admin.dealer_channel_subsidies.pay') + ) { + $actions->append(new DealerChannelSubsidyPay()); + } + }); + + $grid->filter(function (Grid\Filter $filter) { + $filter->panel(); + + $filter->equal('user.phone', '手机号')->width(3); + $filter->where('status', function ($query) { + switch ($this->input) { + case 'pending': + $query->whereNull('settle_at')->where('status', DealerEarningStatus::Pending); + break; + + case 'paying': + $query->whereNotNull('settle_at')->where('status', DealerEarningStatus::Pending); + break; + + case 'completed': + $query->where('status', DealerEarningStatus::Completed); + break; + } + }, '状态')->select([ + 'pending' => '待结算', + 'paying' => '待付款', + 'completed' => '已完成', + ])->width(3); + $filter->between('settle_at', '结算时间')->datetime()->width(6); + }); + + $grid->header(function ($collection) use ($grid) { + return tap(new Row(), function ($row) use ($grid) { + $query = DealerEarningModel::query(); + + $grid->model()->getQueries()->unique()->each(function ($value) use (&$query) { + if (in_array($value['method'], ['paginate', 'get', 'orderBy', 'orderByDesc'], true)) { + return; + } + + $query = call_user_func_array([$query, $value['method']], $value['arguments'] ?? []); + }); + + $totalAmount = (clone $query)->sum('total_amount'); + $totalFee = (clone $query)->sum('fee'); + + $row->column(3, new InfoBox('补贴金额', $totalAmount, 'fa fa-cny')); + $row->column(3, new InfoBox('手续费', $totalFee, 'fa fa-cny')); + }); + }); + }); + } +} diff --git a/app/Admin/Controllers/DealerController.php b/app/Admin/Controllers/DealerController.php index 48eabee1..f6b3ad39 100644 --- a/app/Admin/Controllers/DealerController.php +++ b/app/Admin/Controllers/DealerController.php @@ -188,7 +188,8 @@ class DealerController extends AdminController $productGrid = Grid::make($builder, function (Grid $grid) { $grid->column('product.name', '商品名称'); $grid->column('product.cover', '商品封面')->image(80, 80); - $grid->column('stock', '剩余库存'); + $grid->column('stock', '库存'); + $grid->column('deposit_stock', '云库存'); $grid->column('created_at', '创建时间'); $grid->column('logs', '库存记录')->display('查看')->modal(function ($modal) { $modal->title('商品'); diff --git a/app/Admin/Controllers/DealerDeliveryBillController.php b/app/Admin/Controllers/DealerDeliveryBillController.php new file mode 100644 index 00000000..ec402590 --- /dev/null +++ b/app/Admin/Controllers/DealerDeliveryBillController.php @@ -0,0 +1,134 @@ +model()->orderBy('id', 'desc'); + + $grid->column('id')->sortable(); + $grid->column('sn', '提货单号'); + $grid->column('user.phone', '手机号'); + $grid->column('user.userInfo.nickname', '昵称'); + $grid->column('shipping_fee', '运费')->prepend('¥'); + $grid->column('pay_way', '支付方式')->display(function ($v) { + return $v?->text(); + })->circleDot(PayWay::colors()); + $grid->column('pay_at', '付款时间')->display(function ($v) { + return $v?->toDateTimeString(); + }); + $grid->column('status', '状态')->display(function ($v) { + return $v?->text(); + })->circleDot(DealerDeliveryBillStatus::colors()); + $grid->column('created_at', '创建时间')->display(function ($v) { + return $v?->toDateTimeString(); + }); + + $grid->actions(function (Grid\Displayers\Actions $actions) { + if (Admin::user()->can('dcat.admin.dealer_orders.show')) { + $actions->append(' 显示   '); + } + }); + + $grid->filter(function (Grid\Filter $filter) { + $filter->panel(); + $filter->like('sn', '提货单号')->width(4); + $filter->like('user.phone', '手机号')->width(4); + $filter->equal('status', '状态')->select(DealerDeliveryBillStatus::texts())->width(4); + $filter->between('pay_at', '付款时间')->dateTime()->width(4); + $filter->between('created_at', '创建时间')->dateTime()->width(4); + }); + }); + } + + /** + * Make a show builder. + * + * @param mixed $id + * + * @return Show + */ + protected function detail($id) + { + return function (Row $row) use ($id) { + $row->column(5, function ($column) use ($id) { + $builder = DealerDeliveryBill::with(['user.userInfo']); + + $column->row(Show::make($id, $builder, function (Show $show) { + $show->field('sn', '提货单号'); + $show->field('user.phone', '手机号'); + $show->field('user.user_info.nickname', '昵称'); + $show->field('shipping_fee', '运费'); + $show->field('remark', '备注'); + $show->field('status', '状态')->as(function () { + return $this->status?->text(); + })->circleDot(DealerDeliveryBillStatus::colors()); + $show->field('created_at')->as(function ($v) { + return $this->created_at->toDateTimeString(); + }); + + $show->divider(); + $show->field('consignee_name', '收货人'); + $show->field('consignee_telephone', '联系方式'); + $show->field('consignee', '收货地址')->as(function () { + return $this->consignee_zone . ' '. $this->consignee_address; + }); + + $show->divider(); + $show->field('pay_sn', '支付单号'); + $show->field('pay_way', '支付方式')->as(function () { + return $this->pay_way?->text(); + })->circleDot(PayWay::colors()); + $show->field('pay_at', '付款时间')->as(function () { + return $this->pay_at?->toDateTimeString(); + }); + $show->field('out_trade_no', '外部交易号'); + + + $show->panel() + ->tools(function (Show\Tools $tools) use ($show) { + $tools->disableEdit(); + $tools->disableDelete(); + }); + })); + }); + $row->column(7, function ($column) use ($id) { + $repository = DealerDeliveryProduct::with(['product']); + + $column->row(Box::make('提货单商品', Grid::make($repository, function (Grid $grid) use ($id) { + $grid->model()->where('delivery_bill_id', $id); + + $grid->column('product.name', '名称'); + $grid->column('product.cover', '封面')->image(50, 50); + $grid->column('qty', '数量'); + $grid->disableActions(); + $grid->disablePagination(); + $grid->disableRefreshButton(); + }))); + }); + }; + } +} diff --git a/app/Admin/Controllers/DealerManageSubsidyController.php b/app/Admin/Controllers/DealerManageSubsidyController.php index e643f44e..dc445223 100644 --- a/app/Admin/Controllers/DealerManageSubsidyController.php +++ b/app/Admin/Controllers/DealerManageSubsidyController.php @@ -62,8 +62,8 @@ class DealerManageSubsidyController extends AdminController return "
".nl2br($this->remark).'
'; }); $grid->column('status', '状态')->display(function ($v) { - return '  '.$v->text(); - }); + return $v->text(); + })->circleDot(DealerManageSubsidyStatus::colors()); $grid->column('earning.pay_at', '付款时间'); $grid->showRowSelector(); @@ -123,45 +123,5 @@ class DealerManageSubsidyController extends AdminController }); }); }); - - return Grid::make($builder, function (Grid $grid) { - $grid->model()->orderBy('id', 'desc');//默认ID倒叙 - $grid->column('id')->sortable(); - $grid->column('user.phone', '手机号')->copyable(); - $grid->column('lvl', '等级')->display(function () { - return $this->lvl->text(); - }); - $grid->column('order.sn', '订单编号'); - $grid->column('product.name', '商品名称'); - $grid->column('sales_volume', '销量'); - $grid->column('total_amount', '金额'); - $grid->column('order_completed_at', '结算时间')->sortable(); - $grid->column('created_at')->sortable(); - - $grid->disableCreateButton(); - $grid->disableActions(); - $grid->header(function ($collection) use ($grid) { - $query = DealerManageSubsidyLogModel::query(); - - // 拿到表格筛选 where 条件数组进行遍历 - $grid->model()->getQueries()->unique()->each(function ($value) use (&$query) { - if (in_array($value['method'], ['paginate', 'get', 'orderBy', 'orderByDesc'], true)) { - return; - } - - $query = call_user_func_array([$query, $value['method']], $value['arguments'] ?? []); - }); - - // 查出统计数据 - $totalAmount = (clone $query)->sum('total_amount'); - // 自定义组件 - return "
金额:".$totalAmount.' 元
'; - }); - $grid->filter(function (Grid\Filter $filter) { - $filter->panel(false); - $filter->equal('user.phone', '手机号')->width(3); - $filter->between('order_completed_at', '结算时间')->dateTime()->width(7); - }); - }); } } diff --git a/app/Admin/Controllers/DealerManagerSubsidyController.php b/app/Admin/Controllers/DealerManagerSubsidyController.php index ffa4f236..701e7de5 100644 --- a/app/Admin/Controllers/DealerManagerSubsidyController.php +++ b/app/Admin/Controllers/DealerManagerSubsidyController.php @@ -63,8 +63,8 @@ class DealerManagerSubsidyController extends AdminController return "
".nl2br($this->remark).'
'; }); $grid->column('status', '状态')->display(function ($v) { - return '  '.$v->text(); - }); + return $v->text(); + })->circleDot(DealerManagerSubsidyStatus::colors()); $grid->column('earning.pay_at', '付款时间'); $grid->showRowSelector(); diff --git a/app/Admin/Controllers/DealerOrderController.php b/app/Admin/Controllers/DealerOrderController.php index 5c004b67..1d121d5b 100644 --- a/app/Admin/Controllers/DealerOrderController.php +++ b/app/Admin/Controllers/DealerOrderController.php @@ -52,11 +52,12 @@ class DealerOrderController extends AdminController $statusTexts = DealerOrderStatus::texts(); $grid->column('pay_way')->display(function ($v) { - if ($v) { - return '  '.$v->getDealerOrderText(); - } - return ''; - })->filter(DealerOrderPayWayIn::make(PayWay::dealerOrderTexts())); + return $v?->text(); + })->circleDot(PayWay::colors())->filter(DealerOrderPayWayIn::make([ + PayWay::Offline->value => '线下', + PayWay::Wallet->value => '钱包', + PayWay::WxpayH5->value => '微信支付', + ])); $grid->column('order_status')->display(function ($v) { return $this->order_status; @@ -89,7 +90,7 @@ class DealerOrderController extends AdminController $actions->append(new DealerOrderAllocate()); } - if ((empty($actions->row->consignor) || $actions->row->consignor_id == 1)) { + if ((empty($actions->row->consignor))) { if ($actions->row->isPay() && Admin::user()->can('dcat.admin.dealer_orders.paid')) { $actions->append(new DealerOrderPaid()); } @@ -157,27 +158,9 @@ class DealerOrderController extends AdminController $show->field('consignor.phone')->as(function ($value) { return $value ?? '系统'; }); - $show->html(function () { - $content = ''; - - if ($this->pay_way) { - $content = '  '.$this->pay_way->getDealerOrderText(); - } - - return << -
- 支付方式 -
- -
-
-
{$content} 
-
-
- -HTML; - }); + $show->field('pay_way', '支付方式')->as(function () { + return $this->pay_way?->text(); + })->circleDot(PayWay::colors()); $show->field('pay_sn', '支付流水号'); $show->field('pay_time', '支付时间'); $show->field('paied_time'); @@ -263,6 +246,7 @@ HTML; $grid->column('price', '标价')->prepend('¥'); $grid->column('sale_price', '实际售价')->prepend('¥'); $grid->column('qty', '数量'); + $grid->column('deposit_qty', '云数量'); $grid->disableActions(); $grid->disablePagination(); $grid->disableRefreshButton(); diff --git a/app/Admin/Controllers/DealerPurchaseSubsidyController.php b/app/Admin/Controllers/DealerPurchaseSubsidyController.php new file mode 100644 index 00000000..c15f12ee --- /dev/null +++ b/app/Admin/Controllers/DealerPurchaseSubsidyController.php @@ -0,0 +1,186 @@ +model()->settleCompleted()->orderBy('id', 'desc');//默认ID倒叙 + + $grid->column('settle_period', '结算周期')->display(function () { + return $this->start_at->rawFormat('Y/m/d') . '-' . $this->end_at->rawFormat('Y/m/d'); + })->link(function () { + return admin_route('dealer_purchase_logs.index', [ + 'user_phone' => $this->user?->phone, + 'order_completed_at[start]' => $this->start_at->toDateTimeString(), + 'order_completed_at[end]' => $this->end_at->toDateTimeString(), + ]); + }); + $grid->column('user.phone', '手机号')->copyable(); + $grid->column('user.userInfo.nickname', '昵称'); + $grid->column('lvl', '等级')->display(function () { + return $this->lvl->text(); + }); + $grid->column('total_purchase_amount', '进货业绩')->prepend('¥'); + $grid->column('subsidy_rate', '补贴比例')->append('%'); + $grid->column('total_subsidy', '补贴总额')->prepend('¥')->help('补贴总额=进货业绩*补贴比例'); + $grid->column('total_amount', '应得补贴')->prepend('¥'); + $grid->column('fee_rate', '手续费率')->append('%'); + $grid->column('fee', '手续费')->prepend('¥')->help('手续费=应得补贴*手续费率'); + $grid->column('status', '状态')->display(function ($v) { + return $v->text(); + })->circleDot(DealerPurchaseSubsidyStatus::colors()); + + $grid->showRowSelector(); + $grid->tools(function ($tools) { + $tools->batch(function ($batch) { + $batch->disableDelete(); + + if (Admin::user()->can('dcat.admin.dealer_purchase_subsidies.batch_pay')) { + $batch->add(new DealerPurchaseSubsidyBatchPay()); + } + }); + }); + + $grid->actions(function (Grid\Displayers\Actions $actions) { + if (Admin::user()->can('dcat.admin.dealer_purchase_subsidies.show')) { + $actions->append(' 显示   '); + } + + if ($actions->row->isPending() && Admin::user()->can('dcat.admin.dealer_purchase_subsidies.pay')) { + $actions->append(new DealerPurchaseSubsidyPay()); + } + }); + + $grid->header(function ($collection) use ($grid) { + return tap(new Row(), function ($row) use ($grid) { + $query = DealerPurchaseSubsidyModel::query(); + + $grid->model()->getQueries()->unique()->each(function ($value) use (&$query) { + if (in_array($value['method'], ['paginate', 'get', 'orderBy', 'orderByDesc'], true)) { + return; + } + + $query = call_user_func_array([$query, $value['method']], $value['arguments'] ?? []); + }); + + $row->column(3, new InfoBox('进货业绩', (clone $query)->sum('total_purchase_amount'), 'fa fa-cny')); + }); + }); + + $grid->filter(function (Grid\Filter $filter) { + $filter->panel(); + + $filter->equal('user.phone', '手机号')->width(3); + $filter->equal('status', '状态')->select(DealerPurchaseSubsidyStatus::texts())->width(3); + $filter->whereBetween('settle_period', function ($query) { + $start = $this->input['start'] ?? null; + $end = $this->input['end'] ?? null; + + $query->when($start, function ($query, $start) { + $query->where('start_at', '>=', "{$start} 00:00:00"); + }); + + $query->when($end, function ($query, $end) { + $query->where('end_at', '<=', "{$end} 23:59:59"); + }); + }, '结算周期')->date()->width(6); + }); + }); + } + + /** + * Make a show builder. + * + * @param mixed $id + * + * @return Show + */ + protected function detail($id) + { + $row = new Row(); + + $row->column(5, function ($column) use ($id) { + $builder = DealerPurchaseSubsidy::with(['user.userInfo']); + + $column->row(Show::make($id, $builder, function (Show $show) { + $show->field('id'); + $show->field('settle_period', '结算周期')->as(function () { + return $this->start_at->rawFormat('Y/m/d') . '-' . $this->end_at->rawFormat('Y/m/d'); + }); + $show->field('user.phone', '手机号'); + $show->field('user.user_info.nickname', '昵称'); + $show->field('lvl', '经销商等级')->as(function () { + return $this->lvl->text(); + }); + $show->field('total_purchase_amount', '进货业绩')->prepend('¥'); + $show->field('subsidy_rate', '补贴比例')->append('%'); + $show->field('total_subsidy', '补贴总额')->prepend('¥'); + $show->field('total_amount', '应得补贴')->prepend('¥'); + $show->field('fee_rate', '手续费率')->append('%'); + $show->field('fee', '手续费')->prepend('¥'); + $show->field('real_amount', '实得补贴')->prepend('¥'); + $show->field('status', '状态')->as(function () { + return $this->status?->text(); + })->circleDot(DealerPurchaseSubsidyStatus::colors()); + + $show->panel() + ->tools(function (Show\Tools $tools) use ($show) { + $tools->disableEdit(); + $tools->disableDelete(); + }); + })); + }); + $row->column(7, function ($column) use ($id) { + $dealerPurchaseSubsidyLogBox = Box::make('补贴明细', Grid::make(DealerPurchaseSubsidyLog::class, function (Grid $grid) use ($id) { + $grid->model()->where('purchase_subsidy_id', $id); + + $grid->column('id'); + $grid->column('change_amount', '变更金额'); + $grid->column('remark', '备注'); + $grid->column('created_at', '结算时间'); + $grid->disableCreateButton(); + $grid->disableActions(); + $grid->disableRefreshButton(); + })); + + $column->row($dealerPurchaseSubsidyLogBox); + }); + + return $row; + } +} diff --git a/app/Admin/Controllers/OrderController.php b/app/Admin/Controllers/OrderController.php index 86a857ce..cb32b8da 100644 --- a/app/Admin/Controllers/OrderController.php +++ b/app/Admin/Controllers/OrderController.php @@ -2,7 +2,6 @@ namespace App\Admin\Controllers; -use App\Admin\Actions\Grid\CreateOrderPackage; use App\Admin\Actions\Grid\Exports\ShippingOrder as ExportShippingOrder; use App\Admin\Actions\Grid\KuaidiInfo; use App\Admin\Actions\Grid\OrderSetTag; @@ -11,9 +10,11 @@ use App\Admin\Actions\Show\OrderCreatePackage; use App\Admin\Actions\Show\OrderPay; use App\Admin\Actions\Show\OrderReduce; use App\Admin\Actions\Show\OrderRemark; +use App\Admin\Extensions\Grid\Tools\Order\ExportProduct; use App\Admin\Renderable\Grid\Filter\OrderStatusIn; use App\Admin\Renderable\PackageProductSimpleTable; use App\Admin\Repositories\Order; +use App\Enums\PayWay; use App\Models\Order as OrderModel; use App\Models\OrderLog; use App\Models\OrderPackage; @@ -25,13 +26,93 @@ use Dcat\Admin\Form; use Dcat\Admin\Grid; use Dcat\Admin\Grid\Column; use Dcat\Admin\Http\Controllers\AdminController; +use Dcat\Admin\Layout\Content; use Dcat\Admin\Layout\Row; use Dcat\Admin\Show; use Dcat\Admin\Widgets\Box; +use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\Request; class OrderController extends AdminController { + /** + * Index interface. + * + * @param Content $content + * @return Content + */ + public function index(Content $content) + { + switch (request()->query('_export')) { + case 'product': + if (! Admin::user()->can('dcat.admin.orders.export_order_products')) { + throw new AuthorizationException('没有操作权限'); + } + + $query = OrderModel::with(['user.userInfo', 'products']); + + $grid = $this->grid(); + $grid->processFilter(); + + foreach ($grid->model()->getQueries() as $condition) { + if (in_array($condition['method'], ['paginate', 'get', 'orderBy', 'orderByDesc'], true)) { + continue; + } + + call_user_func_array([$query, $condition['method']], $condition['arguments'] ?? []); + } + + return response()->streamDownload(function () use ($query) { + $writer = WriterEntityFactory::createXLSXWriter(); + $writer->openToBrowser('订单商品'.time().'.xlsx'); + + $writer->addRow(WriterEntityFactory::createRowFromArray([ + '商品ID', + '商品名称', + '数量', + '价格', + '金额', + '待发货数量', + '所属订单', + '下单手机', + '支付方式', + '付款时间', + '订单状态', + '下单时间', + '是否换货', + ])); + + $query->lazyById()->each(function ($order) use ($writer) { + foreach ($order->products as $product) { + $writer->addRow( + WriterEntityFactory::createRowFromArray([ + $product->sku_id, + $product->name, + $product->quantity, + bcdiv($product->sell_price, '100', 2), + bcdiv($product->total_amount, '100', 2), + $product->remain_quantity, + $order->sn, + $order->user->phone, + $order->pay_way?->text(), + $order->pay_at?->toDateTimeString(), + $order->order_status_text, + $order->created_at?->toDateTimeString(), + $order->is_change ? '是' : '否', + ]) + ); + } + }); + + $writer->close(); + }); + + break; + } + + return parent::index($content); + } + /** * Make a grid builder. * @@ -39,106 +120,94 @@ class OrderController extends AdminController */ protected function grid() { - $builder = Order::with(['user', 'tags']); + $grid = new Grid(Order::with(['user', 'tags'])); - return Grid::make($builder, function (Grid $grid) { - $grid->column('id')->sortable()->if(function () { - return Admin::user()->can('dcat.admin.orders.show'); - })->then(function (Column $column) { - $column->link(function ($value) { - return admin_route('orders.show', ['order' => $value]); - }); - }); + $grid->setResource('orders'); + $grid->model()->orderBy('id', 'desc'); - $grid->tools(function (Grid\Tools $tools) { - //设置规格 - if (Admin::user()->can('dcat.admin.orders.export_shipping_orders')) { - $tools->append(new ExportShippingOrder()); - } + $grid->column('id')->sortable()->if(function () { + return Admin::user()->can('dcat.admin.orders.show'); + })->then(function (Column $column) { + $column->link(function ($value) { + return admin_route('orders.show', ['order' => $value]); }); - $grid->column('sn')->copyable(); - $grid->column('tags', '标签')->display(function ($tags) { - $array = []; - foreach ($this->tags as $key => $tag) { - $array[] = $tag->name; - } - return $array; - })->label(); - $grid->column('user.phone')->copyable(); - $grid->column('total_amount')->display(function ($value) { - return bcdiv($value, 100, 2); - })->prepend('¥'); - $grid->column('order_status')->display(function ($value) { - return $this->order_status; - })->using([ + }); + $grid->column('sn')->copyable(); + $grid->column('tags', '标签')->display(function ($tags) { + return $tags->implode('name'); + })->label(); + $grid->column('user.phone')->copyable(); + $grid->column('total_amount')->display(function ($value) { + return bcdiv($value, 100, 2); + })->prepend('¥'); + $grid->column('order_status')->display(function ($value) { + return $this->order_status; + })->using([ + 0=>'待付款', + 1=>'待发货', + 2=>'发货中', + 3=>'已发货', + 9=>'已完成', + 10=>'已取消', + ])->dot([ + 0=>'primary', + 1=>'warning', + 2=>'danger', + 3=>'success', + 9=>'success', + 10=>'#b3b9bf', + ])->filter( + OrderStatusIn::make([ 0=>'待付款', 1=>'待发货', 2=>'发货中', 3=>'已发货', 9=>'已完成', 10=>'已取消', - ])->dot([ - 0=>'primary', - 1=>'warning', - 2=>'danger', - 3=>'success', - 9=>'success', - 10=>'#b3b9bf', - ])->filter( - OrderStatusIn::make([ - 0=>'待付款', - 1=>'待发货', - 2=>'发货中', - 3=>'已发货', - 9=>'已完成', - 10=>'已取消', - ]) - ); - $grid->column('pay_way')->display(function ($v) { - if ($v) { - return '  '.$v->getMallOrderText(); - } + ]) + ); + $grid->column('pay_way')->display(function ($v) { + return $v?->mallText(); + })->circleDot(PayWay::colors()); + $grid->column('pay_at'); + $grid->column('created_at')->sortable(); - return ''; - }); - $grid->column('pay_at'); - // $grid->column('consignee_name'); - // $grid->column('consignee_telephone'); - // $grid->column('consignee_zone'); - // $grid->column('consignee_address'); - - $grid->column('created_at')->sortable(); - - $grid->model()->orderBy('created_at', 'desc'); - - $grid->actions(function (Grid\Displayers\Actions $actions) { - if (Admin::user()->can('dcat.admin.orders.show')) { - $actions->disableView(false); - } - - if (Admin::user()->can('dcat.admin.orders.tags')) { - $actions->append(new OrderSetTag()); - } - if (Admin::user()->can('dcat.admin.distribution_pre_incomes.index')) { - $actions->append(' 预收益明细'); - } - // $actions->append(new CreateOrderPackage()); - }); - - $grid->setResource('orders'); - - $grid->filter(function (Grid\Filter $filter) { - $filter->panel(); - $filter->like('sn')->width(3); - $filter->like('user.phone')->width(3); - $filter->where('tags', function ($query) { - $query->whereHas('tags', function ($q) { - $q->whereIn('tags.id', $this->input); - }); - }, '标签')->multipleSelect(Tag::orderTag()->pluck('name', 'id'))->width(3); - $filter->between('created_at')->dateTime()->width(7); - }); + $grid->filter(function (Grid\Filter $filter) { + $filter->panel(); + $filter->like('sn')->width(3); + $filter->like('user.phone')->width(3); + $filter->where('tags', function ($query) { + $query->whereHas('tags', function ($q) { + $q->whereIn('tags.id', $this->input); + }); + }, '标签')->multipleSelect(Tag::orderTag()->pluck('name', 'id'))->width(3); + $filter->between('created_at')->dateTime()->width(7); }); + + $grid->tools(function (Grid\Tools $tools) { + if (Admin::user()->can('dcat.admin.orders.export_shipping_orders')) { + $tools->append(new ExportShippingOrder()); + } + + if (Admin::user()->can('dcat.admin.orders.export_order_products')) { + $tools->append(new ExportProduct()); + } + }); + + $grid->actions(function (Grid\Displayers\Actions $actions) { + if (Admin::user()->can('dcat.admin.orders.show')) { + $actions->disableView(false); + } + + if (Admin::user()->can('dcat.admin.orders.tags')) { + $actions->append(new OrderSetTag()); + } + if (Admin::user()->can('dcat.admin.distribution_pre_incomes.index')) { + $actions->append(' 预收益明细'); + } + }); + + return $grid; } /** @@ -180,15 +249,9 @@ class OrderController extends AdminController return $this->tags->pluck('name'); })->label(); $show->field('pay_at'); - $show->field('pay_way')->unescape()->as(function () { - $content = ''; - - if ($this->pay_way) { - $content = '  '.$this->pay_way->getMallOrderText(); - } - - return $content; - }); + $show->field('pay_way', '支付方式')->as(function () { + return $this->pay_way?->text(); + })->circleDot(PayWay::colors()); }); $show->row(function (Show\Row $show) { $show->width(6)->field('consignee_name'); diff --git a/app/Admin/Controllers/SettingController.php b/app/Admin/Controllers/SettingController.php index 58b3237c..045c6c9f 100644 --- a/app/Admin/Controllers/SettingController.php +++ b/app/Admin/Controllers/SettingController.php @@ -4,6 +4,7 @@ namespace App\Admin\Controllers; use App\Admin\Forms\Settings\Android; use App\Admin\Forms\Settings\App; +use App\Admin\Forms\Settings\Custom; use App\Admin\Forms\Settings\Dealer; use App\Admin\Forms\Settings\Distribution; use App\Admin\Forms\Settings\Ios; @@ -118,6 +119,7 @@ class SettingController extends AdminController $tab->addLink('Android配置', admin_route('settings.index', ['type'=>'ios'])); $tab->addLink('快递100配置', admin_route('settings.index', ['type'=>'kuaidi100'])); $tab->addLink('Uni-push配置', admin_route('settings.index', ['type'=>'unipush'])); + $tab->addLink('自定义配置', admin_route('settings.index', ['type'=>'custom'])); break; case 'distribution': $tab->addLink('系统配置', admin_route('settings.index', ['type'=>'app'])); @@ -128,6 +130,7 @@ class SettingController extends AdminController $tab->addLink('Android配置', admin_route('settings.index', ['type'=>'android'])); $tab->addLink('快递100配置', admin_route('settings.index', ['type'=>'kuaidi100'])); $tab->addLink('Uni-push配置', admin_route('settings.index', ['type'=>'unipush'])); + $tab->addLink('自定义配置', admin_route('settings.index', ['type'=>'custom'])); break; case 'dealer': $tab->addLink('系统配置', admin_route('settings.index', ['type'=>'app'])); @@ -138,6 +141,7 @@ class SettingController extends AdminController $tab->addLink('Android配置', admin_route('settings.index', ['type'=>'android'])); $tab->addLink('快递100配置', admin_route('settings.index', ['type'=>'kuaidi100'])); $tab->addLink('Uni-push配置', admin_route('settings.index', ['type'=>'unipush'])); + $tab->addLink('自定义配置', admin_route('settings.index', ['type'=>'custom'])); break; case 'withdraw': $tab->addLink('系统配置', admin_route('settings.index', ['type'=>'app'])); @@ -148,6 +152,7 @@ class SettingController extends AdminController $tab->addLink('Android配置', admin_route('settings.index', ['type'=>'android'])); $tab->addLink('快递100配置', admin_route('settings.index', ['type'=>'kuaidi100'])); $tab->addLink('Uni-push配置', admin_route('settings.index', ['type'=>'unipush'])); + $tab->addLink('自定义配置', admin_route('settings.index', ['type'=>'custom'])); break; case 'ios': $tab->addLink('系统配置', admin_route('settings.index', ['type'=>'app'])); @@ -158,6 +163,7 @@ class SettingController extends AdminController $tab->addLink('Android配置', admin_route('settings.index', ['type'=>'android'])); $tab->addLink('快递100配置', admin_route('settings.index', ['type'=>'kuaidi100'])); $tab->addLink('Uni-push配置', admin_route('settings.index', ['type'=>'unipush'])); + $tab->addLink('自定义配置', admin_route('settings.index', ['type'=>'custom'])); break; case 'android': $tab->addLink('系统配置', admin_route('settings.index', ['type'=>'app'])); @@ -168,6 +174,7 @@ class SettingController extends AdminController $tab->add('Android配置', new Android(), true); $tab->addLink('快递100配置', admin_route('settings.index', ['type'=>'kuaidi100'])); $tab->addLink('Uni-push配置', admin_route('settings.index', ['type'=>'unipush'])); + $tab->addLink('自定义配置', admin_route('settings.index', ['type'=>'custom'])); break; case 'kuaidi100': $tab->addLink('系统配置', admin_route('settings.index', ['type'=>'app'])); @@ -178,6 +185,7 @@ class SettingController extends AdminController $tab->addLink('Android配置', admin_route('settings.index', ['type'=>'android'])); $tab->add('快递100配置', new Kuaidi100(), true); $tab->addLink('Uni-push配置', admin_route('settings.index', ['type'=>'unipush'])); + $tab->addLink('自定义配置', admin_route('settings.index', ['type'=>'custom'])); break; case 'unipush': $tab->addLink('系统配置', admin_route('settings.index', ['type'=>'app'])); @@ -188,6 +196,18 @@ class SettingController extends AdminController $tab->addLink('Android配置', admin_route('settings.index', ['type'=>'android'])); $tab->addLink('快递100配置', admin_route('settings.index', ['type'=>'kuaidi100'])); $tab->add('Uni-push配置', new Unipush(), true); + $tab->addLink('自定义配置', admin_route('settings.index', ['type'=>'custom'])); + break; + case 'custom': + $tab->addLink('系统配置', admin_route('settings.index', ['type'=>'app'])); + // $tab->addLink('会员奖励配置', admin_route('settings.index', ['type'=>'distribution'])); + $tab->addLink('经销商配置', admin_route('settings.index', ['type'=>'dealer'])); + $tab->addLink('提现配置', admin_route('settings.index', ['type'=>'withdraw'])); + $tab->addLink('Ios配置', admin_route('settings.index', ['type'=>'ios'])); + $tab->addLink('Android配置', admin_route('settings.index', ['type'=>'android'])); + $tab->addLink('快递100配置', admin_route('settings.index', ['type'=>'kuaidi100'])); + $tab->addLink('Uni-push配置', admin_route('settings.index', ['type'=>'unipush'])); + $tab->add('自定义配置', new Custom(), true); break; default: break; diff --git a/app/Admin/Extensions/Column/CircleDot.php b/app/Admin/Extensions/Column/CircleDot.php new file mode 100644 index 00000000..33c6dad9 --- /dev/null +++ b/app/Admin/Extensions/Column/CircleDot.php @@ -0,0 +1,50 @@ +value)) { + return; + } + + if ($value instanceof UnitEnum) { + $value = $value->value; + } + + $background = $this->background($options, $default); + + return "  {$value}"; + } + + /** + * 获取圆点的背景色 + * + * @param array $options + * @param string $default + * @return string + */ + protected function background(array $options, string $default = 'default'): string + { + $original = $this->column->getOriginal(); + + $style = is_null($original) ? $default : Arr::get( + $options, + $original instanceof UnitEnum + ? $original->value + : $original, + $default + ); + + $style = $style === 'default' ? 'dark70' : $style; + + return Admin::color()->get($style, $style); + } +} diff --git a/app/Admin/Extensions/Grid/Tools/Order/ExportProduct.php b/app/Admin/Extensions/Grid/Tools/Order/ExportProduct.php new file mode 100644 index 00000000..b092dbdc --- /dev/null +++ b/app/Admin/Extensions/Grid/Tools/Order/ExportProduct.php @@ -0,0 +1,43 @@ +can('dcat.admin.orders.export_order_products'); + } + + /** + * 按钮样式定义,默认 btn btn-white waves-effect + * + * @var string + */ + protected $style = 'btn btn-primary'; + + /** + * 按钮文本 + * + * @return string|void + */ + public function title() + { + return '导出商品'; + } + + protected function script() + { + $this->setHtmlAttribute('data-role', 'order-products-exporter'); + + $url = request()->fullUrlWithQuery(['_export' => 'product']); + + return <<update([ + 'pay_way' => DealerEarning::PAY_WAY_WALLET, 'pay_info' => $earning->getPayInfo(), - 'pay_image' => $input['pay_image']??null, + 'pay_image' => $input['pay_image'] ?? null, 'pay_at' => now(), 'status' => DealerEarningStatus::Completed, ]); diff --git a/app/Admin/Forms/Settings/Custom.php b/app/Admin/Forms/Settings/Custom.php new file mode 100644 index 00000000..f781b6c7 --- /dev/null +++ b/app/Admin/Forms/Settings/Custom.php @@ -0,0 +1,48 @@ +updateOrCreate([ + 'key' => 'custom', + ], ['value' => $input]); + + //清配置缓存 + app(SettingService::class)->cleanCache('custom'); + + return $this + ->response() + ->success('配置更新成功!') + ->refresh(); + } + + /** + * Build a form here. + */ + public function form() + { + $this->keyValue('key_value', '配置')->setKeyLabel('键名')->setValueLabel('键值'); + } + + public function default() + { + $appSettings = Setting::where('key', 'custom')->value('value'); + return [ + 'key_value' => $appSettings['key_value'] ?? [], + ]; + } +} diff --git a/app/Admin/Forms/Settings/Dealer.php b/app/Admin/Forms/Settings/Dealer.php index 0253b089..46ffb3e3 100644 --- a/app/Admin/Forms/Settings/Dealer.php +++ b/app/Admin/Forms/Settings/Dealer.php @@ -39,6 +39,7 @@ class Dealer extends Form public function form() { $this->switch('wxpay_switch', '微信支付'); + $this->currency('delivery_bill_shipping_fee', '云仓运费')->symbol('¥'); $this->currency('fee_rate', '手续费比例(百分比)')->symbol('%'); $this->number('withdraw_threshold_amount', '起提金额(元)'); $this->currency('withdraw_fee_rate', '提现费率')->symbol('%'); @@ -139,6 +140,7 @@ class Dealer extends Form $dealerSettings = (array) Setting::where('key', 'dealer')->value('value'); return [ 'wxpay_switch'=> $dealerSettings['wxpay_switch'] ?? 1, + 'delivery_bill_shipping_fee'=> $dealerSettings['delivery_bill_shipping_fee'] ?? 0, 'fee_rate'=> $dealerSettings['fee_rate'] ?? '', 'withdraw_threshold_amount'=> $dealerSettings['withdraw_threshold_amount'] ?? 0, 'withdraw_fee_rate'=> $dealerSettings['withdraw_fee_rate'] ?? 0, diff --git a/app/Admin/Renderable/DealerUserProductLogSimpleTable.php b/app/Admin/Renderable/DealerUserProductLogSimpleTable.php index a0500170..f64a63e6 100644 --- a/app/Admin/Renderable/DealerUserProductLogSimpleTable.php +++ b/app/Admin/Renderable/DealerUserProductLogSimpleTable.php @@ -18,16 +18,17 @@ class DealerUserProductLogSimpleTable extends LazyRenderable return Grid::make($builder, function (Grid $grid) { $grid->column('product.name', '商品名称'); $grid->column('remark', '备注'); + $grid->column('is_deposit', '范围')->using([ + 0=>'本地库存', + 1=>'云库存', + ]); $grid->column('qty', '变动数量')->display(function () { - return (in_array($this->type, [ - DealerUserProductLog::TYPE_ORDER_IN, - DealerUserProductLog::TYPE_ADMIN_IN, - ]) ? '+' : '-').$this->qty.$this->product?->unit; + return $this->qty_format.$this->product?->unit; }); $grid->column('created_at', '创建时间'); // $grid->withBorder(); - $grid->model()->orderBy('created_at', 'desc'); + $grid->model()->orderBy('id', 'desc'); $grid->disableRefreshButton(); $grid->disableActions(); }); diff --git a/app/Admin/Repositories/DealerDeliveryBill.php b/app/Admin/Repositories/DealerDeliveryBill.php new file mode 100644 index 00000000..347fe149 --- /dev/null +++ b/app/Admin/Repositories/DealerDeliveryBill.php @@ -0,0 +1,16 @@ +isSettled()) { + throw new BizException('经销商收益还未结算'); + } + + if (! $dealerEarning->isPending()) { + throw new BizException('经销商收益状态不是待付款'); + } + + switch (Relation::getMorphedModel($dealerEarning->earningable_type)) { + // 管理津贴 + case DealerManageSubsidy::class: + if (! $dealerEarning->earningable->isPending()) { + throw new BizException('管理津贴状态不是待付款'); + } + + $dealerEarning->earningable->update([ + 'status' => DealerManageSubsidyStatus::Completed, + ]); + + (new WalletService())->changeBalance( + $dealerEarning->user, + $dealerEarning->total_earnings, + DealerWalletAction::ManageSubsidyIn, + '收入-管理津贴', + $dealerEarning + ); + break; + + // 管理者津贴 + case DealerManagerSubsidy::class: + if (! $dealerEarning->earningable->isPending()) { + throw new BizException('管理者津贴状态不是待付款'); + } + + $dealerEarning->earningable->update([ + 'status' => DealerManagerSubsidyStatus::Completed, + ]); + + (new WalletService())->changeBalance( + $dealerEarning->user, + $dealerEarning->total_earnings, + DealerWalletAction::ManagerSubsidyIn, + '收入-管理者津贴', + $dealerEarning + ); + break; + + // 管理者津贴 + case DealerPurchaseSubsidy::class: + if (! $dealerEarning->earningable->isPending()) { + throw new BizException('进货补贴状态不是待付款'); + } + + $dealerEarning->earningable->update([ + 'status' => DealerPurchaseSubsidyStatus::Completed, + ]); + + (new WalletService())->changeBalance( + $dealerEarning->user, + $dealerEarning->total_earnings, + DealerWalletAction::PurchaseSubsidyIn, + '收入-进货补贴', + $dealerEarning + ); + break; + + case DealerChannelSubsidyLog::class: + (new WalletService())->changeBalance( + $dealerEarning->user, + $dealerEarning->total_earnings, + DealerWalletAction::ChannelSubsidyIn, + '收入-渠道补贴', + $dealerEarning + ); + break; + + default: + throw new BizException('经销商收入异常'); + break; + } + + $dealerEarning->update([ + 'pay_way' => DealerEarning::PAY_WAY_WALLET, + 'pay_at' => now(), + 'pay_info' => null, + 'status' => DealerEarningStatus::Completed, + ]); + } +} diff --git a/app/Admin/bootstrap.php b/app/Admin/bootstrap.php index d69d8032..4a3f1977 100644 --- a/app/Admin/bootstrap.php +++ b/app/Admin/bootstrap.php @@ -1,5 +1,6 @@ disableRowSelector(); @@ -60,6 +63,27 @@ Editor::resolving(function (Editor $editor) { }); ShowField::extend('showLabel', Label::class); +ShowField::macro('circleDot', function ($options = [], $default = 'default') { + return $this->unescape()->prepend(function ($_, $original) use ($options, $default) { + if (is_null($original)) { + return; + } + + $style = Arr::get( + $options, + $original instanceof UnitEnum + ? $original->value + : $original, + $default + ); + + $style = $style === 'default' ? 'dark70' : $style; + + $background = Admin::color()->get($style, $style); + + return "  "; + }); +}); Admin::style( <<<'CSS' diff --git a/app/Admin/routes.php b/app/Admin/routes.php index ae1aa6fa..a679fc24 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -186,6 +186,9 @@ Route::group([ $router->get('dealer-earnings-manager', 'DealerEarningController@index')->name('dealer_earnings.manager'); $router->get('dealer-earnings-purchase', 'DealerEarningController@index')->name('dealer_earnings.purchase'); + // 签约渠道补贴 + $router->get('dealer-channel-subsidies', 'DealerChannelSubsidyController@index')->name('dealer_channel_subsidies.index'); + // 管理者津贴 $router->get('dealer-manager-subsidies', 'DealerManagerSubsidyController@index')->name('dealer_manager_subsidies.index'); $router->get('dealer-manager-sales-logs', 'DealerManagerSalesLogController@index')->name('dealer_manager_sales_logs.index'); @@ -194,6 +197,8 @@ Route::group([ $router->get('dealer-manage-subsidies', 'DealerManageSubsidyController@index')->name('dealer_manage_subsidies.index'); $router->get('dealer-manage-subsidy-logs', 'DealerManageSubsidyLogController@index')->name('dealer_manage_subsidy_logs.index'); + $router->get('dealer-purchase-subsidies', 'DealerPurchaseSubsidyController@index')->name('dealer_purchase_subsidies.index'); + $router->get('dealer-purchase-subsidies/{dealer_purchase_subsidy}', 'DealerPurchaseSubsidyController@show')->name('dealer_purchase_subsidies.show'); $router->get('dealer-purchase-logs', 'DealerPurchaseLogController@index')->name('dealer_purchase_logs.index'); //批零余额提现 @@ -202,6 +207,10 @@ Route::group([ 'index', 'show', ])->names('dealer_wallet_to_bank_logs'); + // + $router->get('dealer-delivery-bills', 'DealerDeliveryBillController@index')->name('dealer_delivery_bills.index'); + $router->get('dealer-delivery-bills/{dealer_delivery_bill}', 'DealerDeliveryBillController@show')->name('dealer_delivery_bills.show'); + /** api接口 **/ $router->get('api/product-categories', 'ProductCategoryController@categories')->name('api.product_categories'); $router->get('api/product-group-details', 'ProductGroupController@details')->name('api.product_group_details'); diff --git a/app/Constants/OrderStatus.php b/app/Constants/OrderStatus.php index 8d10077e..ab61ff4f 100644 --- a/app/Constants/OrderStatus.php +++ b/app/Constants/OrderStatus.php @@ -11,4 +11,14 @@ class OrderStatus public const SHIPPED = 3; // 已发货/待收货 public const COMPLETED = 9; // 已完成 public const CANCELLED = 10; // 已取消 + + public static $statusTexts = [ + self::UNKNOWN => '其它', + self::PENDING => '待付款', + self::WAIT_SHIPPING => '待发货', + self::SHIPPING => '发货中', + self::SHIPPED => '待收货', + self::COMPLETED => '已完成', + self::CANCELLED => '已取消', + ]; } diff --git a/app/Endpoint/Api/Filters/DealerDeliveryBillFilter.php b/app/Endpoint/Api/Filters/DealerDeliveryBillFilter.php new file mode 100644 index 00000000..52c8eef3 --- /dev/null +++ b/app/Endpoint/Api/Filters/DealerDeliveryBillFilter.php @@ -0,0 +1,26 @@ +where('status', DealerDeliveryBillStatus::Pending); + break; + + case 'paid': + $this->where('status', DealerDeliveryBillStatus::Paid); + break; + + case 'cancelled': + $this->where('status', DealerDeliveryBillStatus::Cancelled); + break; + } + } +} diff --git a/app/Endpoint/Api/Http/Controllers/Dealer/ConfigurationController.php b/app/Endpoint/Api/Http/Controllers/Dealer/ConfigurationController.php index 644a9cec..043e88b9 100644 --- a/app/Endpoint/Api/Http/Controllers/Dealer/ConfigurationController.php +++ b/app/Endpoint/Api/Http/Controllers/Dealer/ConfigurationController.php @@ -11,6 +11,8 @@ class ConfigurationController extends Controller { return response()->json([ 'wxpay_switch' => app_settings('dealer.wxpay_switch', 0), + 'delivery_bill_shipping_fee' => app_settings('dealer.delivery_bill_shipping_fee', 0), + 'withdraw_fee_rate' => app_settings('dealer.withdraw_fee_rate', 0), ]); } } diff --git a/app/Endpoint/Api/Http/Controllers/Dealer/DealerDeliveryBillController.php b/app/Endpoint/Api/Http/Controllers/Dealer/DealerDeliveryBillController.php new file mode 100644 index 00000000..49240e0b --- /dev/null +++ b/app/Endpoint/Api/Http/Controllers/Dealer/DealerDeliveryBillController.php @@ -0,0 +1,178 @@ +user()->dealerDeliveryBills() + ->filter($request->all()) + ->latest('id') + ->simplePaginate($perPage); + + return DealerDeliveryBillResource::collection($deliveryBills); + } + + /** + * 创建提货单 + * + * @param \Illuminate\Http\Request $request + * @param \App\Services\Dealer\DealerDeliveryBillService $dealerDeliveryBillService + * @return \Illuminate\Http\JsonResponse + */ + public function store(Request $request, DealerDeliveryBillService $dealerDeliveryBillService) + { + $input = $request->validate([ + 'shipping_address_id' => ['bail', 'required'], + 'products' => ['bail', 'required', 'array'], + 'products.*.id' => ['bail', 'required', 'int'], + 'products.*.qty' => ['bail', 'required', 'int', 'min:1'], + 'remark' => ['bail', 'nullable', 'string', 'max:255'], + ], [], [ + 'shipping_address_id' => '收货地址', + 'products' => '商品', + 'remark' => '备注', + ]); + + $user = $request->user(); + + $shippingAddress = $user->shippingAddresses()->findOrFail($input['shipping_address_id']); + + try { + DB::beginTransaction(); + + $deliveryBill = $dealerDeliveryBillService->create( + $user, + $input['products'], + [ + 'name' => $shippingAddress->consignee, + 'telephone' => $shippingAddress->telephone, + 'zone' => $shippingAddress->zone, + 'address' => $shippingAddress->address, + ], + $input['remark'] ?? null + ); + + DB::commit(); + } catch (Throwable $e) { + DB::rollBack(); + + throw $e; + } + + $deliveryBill->load(['deliveryProducts.product']); + + return DealerDeliveryBillResource::make($deliveryBill); + } + + /** + * 提货单详情 + * + * @param int $id + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function show($id, Request $request) + { + $deliveryBill = $request->user()->dealerDeliveryBills()->findOrFail($id); + $deliveryBill->load(['deliveryProducts.product']); + + return DealerDeliveryBillResource::make($deliveryBill); + } + + /** + * 提货单支付 + * + * @param int $id + * @param \Illuminate\Http\Request $request + * @param \App\Services\Dealer\DealerDeliveryBillService $dealerDeliveryBillService + * @return \Illuminate\Http\JsonResponse + */ + public function pay($id, Request $request, DealerDeliveryBillService $dealerDeliveryBillService) + { + $input = $request->validate([ + 'pay_way' => ['bail', 'required', 'string'], + 'pay_password' => ['bail', 'required_if:pay_way,wallet'], + 'products'=> ['array'], + ], [ + 'pay_password.required_if' => '支付密码 不能为空。', + ], [ + 'pay_way' => '支付方式', + 'pay_password' => '支付密码', + ]); + + $payWay = PayWay::tryFrom($input['pay_way']); + + if (! in_array($payWay, [PayWay::Wallet, PayWay::WxpayH5, PayWay::WxpayJsApi])) { + throw new BizException('支付方式 非法'); + } + + $user = $request->user(); + + $deliveryBill = $user->dealerDeliveryBills()->findOrFail($id); + + if ($payWay === PayWay::Wallet && !$user->wallet?->verifyPassword($input['pay_password'])) { + throw new PayPasswordIncorrectException(); + } + + try { + DB::beginTransaction(); + + $data = $dealerDeliveryBillService->pay($deliveryBill, $payWay); + + DB::commit(); + } catch (BizException $e) { + DB::rollBack(); + + throw $e; + } catch (Throwable $e) { + DB::rollBack(); + + report($e); + + throw new BizException('支付失败,请重试'); + } + + return response()->json($data); + } + + /** + * 取消提货单 + * + * @param int $id + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function cancel($id, Request $request) + { + $user = $request->user(); + + DB::transaction(function () use ($id, $user) { + (new DealerDeliveryBillService())->cancel( + $user->dealerDeliveryBills()->lockForUpdate()->findOrFail($id) + ); + }); + + return response()->noContent(); + } +} diff --git a/app/Endpoint/Api/Http/Controllers/Dealer/OrderController.php b/app/Endpoint/Api/Http/Controllers/Dealer/OrderController.php index 82339896..a64f7ba9 100644 --- a/app/Endpoint/Api/Http/Controllers/Dealer/OrderController.php +++ b/app/Endpoint/Api/Http/Controllers/Dealer/OrderController.php @@ -5,11 +5,13 @@ namespace App\Endpoint\Api\Http\Controllers\Dealer; use App\Endpoint\Api\Http\Controllers\Controller; use App\Endpoint\Api\Http\Resources\Dealer\OrderResource; use App\Endpoint\Api\Http\Resources\Dealer\OrderSimpleResource; +use App\Enums\DealerLvl; use App\Enums\PayWay; use App\Exceptions\BizException; use App\Exceptions\PayPasswordIncorrectException; use App\Helpers\Paginator as PaginatorHelper; use App\Models\DealerOrder; +use App\Models\DealerOrderProduct; use App\Models\DealerProduct; use App\Services\Dealer\OrderService; use Illuminate\Database\QueryException; @@ -217,6 +219,7 @@ class OrderController extends Controller 'pay_image' => ['bail', 'string'], 'pay_way' => ['bail', 'string'], 'pay_password' => ['bail', 'required_if:pay_way,wallet'], + 'products'=> ['array'], ], [ 'pay_password.required_if' => '支付密码 不能为空。', ], [ @@ -224,10 +227,9 @@ class OrderController extends Controller 'pay_way' => '支付方式', 'pay_password' => '支付密码', ]); - $payWay = PayWay::tryFrom($input['pay_way'] ?? 'offline'); - if (! in_array($payWay, [PayWay::Offline, PayWay::Wallet, PayWay::WxpayH5])) { + if (! in_array($payWay, [PayWay::Offline, PayWay::Wallet, PayWay::WxpayH5, PayWay::WxpayJsApi])) { throw new BizException('支付方式 非法'); } @@ -244,6 +246,7 @@ class OrderController extends Controller break; case PayWay::WxpayH5: + case PayWay::WxpayJsApi: if ($order->consignor !== null) { throw new BizException('订单不是签约订单'); } @@ -254,6 +257,29 @@ class OrderController extends Controller try { DB::beginTransaction(); + + //签约单-处理云库存 + if ($order->consignor === null) { + //处理云库存设置,如果没有设置则默认云仓库为0。 + $products = $input['products'] ?? []; + if ($products) { + $num = 0; + foreach ($order->products as $product) { + //更新云库存 + if (isset($products[$product->id]) && ($product->qty + $product->deposit_qty) >= $products[$product->id]) { + DealerOrderProduct::where('id', $product->id)->update([ + 'qty' => $products[$product->id], + 'deposit_qty' =>($product->qty + $product->deposit_qty) - $products[$product->id], + ]); + $num += $products[$product->id]; + } + } + if ($num < 20) { + throw new BizException('首次发货最少20盒'); + } + } + } + $data = $orderService->pay($order, $payWay, $input['pay_image'] ?? null); DB::commit(); @@ -273,7 +299,7 @@ class OrderController extends Controller } /** - * 确认收款+发货 + * 确认收款 * * @return void */ @@ -288,7 +314,8 @@ class OrderController extends Controller try { DB::beginTransaction(); $orderService->paidOrder($order);//确认收款 - $orderService->shippingOrder($order);//确认发货 + //3-14号取消确认收款后自动发货 + // $orderService->shippingOrder($order);//确认发货 DB::commit(); } catch (QueryException $e) { DB::rollBack(); @@ -315,15 +342,32 @@ class OrderController extends Controller public function shippingOrder($id, Request $request, OrderService $orderService) { $order = DealerOrder::findOrFail($id); - $userId = $request->user()->id; + $user = $request->user(); //不是发货人 - if (!$order->isConsignor($userId)) { + if (!$order->isConsignor($user->id)) { throw new BizException('订单未找到'); } + $deliveryBill = null; try { DB::beginTransaction(); - $orderService->shippingOrder($order);//确认发货 + //如果发货人是签约经销商, 则可以使用云仓库发货 + if ($user->dealer->lvl?->value >= DealerLvl::Contracted->value) { + $deliveryBill = $orderService->shippingOrderByDeposit($order); + if ($deliveryBill) { + $order->refresh(); + //扣除本地库存 + $orderService->orderOutQty($order); + } else { + $orderService->shippingOrder($order);//确认发货 + } + } else { + $orderService->shippingOrder($order);//确认发货 + } DB::commit(); + } catch (BizException $e) { + DB::rollBack(); + $e = new BizException($e->getMessage()); + throw $e; } catch (QueryException $e) { DB::rollBack(); if (strpos($e->getMessage(), 'Numeric value out of range') !== false) { @@ -335,7 +379,10 @@ class OrderController extends Controller report($th); throw new BizException('操作失败,请刷新后再试'); } - return response()->noContent(); + + return response()->json([ + 'delivery_bill_id'=>$deliveryBill?->id, + ]); } /** @@ -346,9 +393,9 @@ class OrderController extends Controller public function shippingedOrder($id, Request $request, OrderService $orderService) { $order = DealerOrder::findOrFail($id); - $userId = $request->user()->id; + $user = $request->user(); //不是收货人 - if (!$order->isUser($userId)) { + if (!$order->isUser($user->id)) { throw new BizException('订单未找到'); } try { diff --git a/app/Endpoint/Api/Http/Controllers/Dealer/UserProductController.php b/app/Endpoint/Api/Http/Controllers/Dealer/UserProductController.php index 702736a9..693fd92b 100644 --- a/app/Endpoint/Api/Http/Controllers/Dealer/UserProductController.php +++ b/app/Endpoint/Api/Http/Controllers/Dealer/UserProductController.php @@ -37,7 +37,7 @@ class UserProductController extends Controller { $list = $request->user()->dealerProductLogs()->filter($request->all()) ->with('product') - ->orderBy('created_at', 'desc') + ->orderBy('id', 'desc') ->simplePaginate(Paginator::resolvePerPage('per_page', 20, 50)); return UserProductLogResource::collection($list); } diff --git a/app/Endpoint/Api/Http/Controllers/SettingController.php b/app/Endpoint/Api/Http/Controllers/SettingController.php index cf9d476d..feaa74ad 100644 --- a/app/Endpoint/Api/Http/Controllers/SettingController.php +++ b/app/Endpoint/Api/Http/Controllers/SettingController.php @@ -20,4 +20,17 @@ class SettingController extends Controller 'withdraw'=>app_settings('withdraw'), ]); } + + public function custom(Request $request) + { + $keys = (array) $request->input('keys'); + $custom = app_settings('custom.key_value'); + $keyValue = []; + foreach ($keys as $key) { + $keyValue[$key] = $custom[$key] ?? ''; + } + return response()->json([ + 'values'=>$keyValue, + ]); + } } diff --git a/app/Endpoint/Api/Http/Resources/Dealer/DealerDeliveryBillResource.php b/app/Endpoint/Api/Http/Resources/Dealer/DealerDeliveryBillResource.php new file mode 100644 index 00000000..83089e86 --- /dev/null +++ b/app/Endpoint/Api/Http/Resources/Dealer/DealerDeliveryBillResource.php @@ -0,0 +1,34 @@ + $this->id, + 'sn' => $this->sn, + 'shipping_fee' => $this->shipping_fee, + 'remark' => $this->remark, + 'consignee_name' => $this->consignee_name, + 'consignee_telephone' => $this->consignee_telephone, + 'consignee_zone' => $this->consignee_zone, + 'consignee_address' => $this->consignee_address, + 'delivery_products' => DealerDeliveryProductResource::collection($this->whenLoaded('deliveryProducts')), + 'pay_sn' => $this->pay_sn, + 'pay_way' => $this->pay_way, + 'pay_at' => $this->pay_at?->toDateTimeString(), + 'status' => $this->status, + 'created_at' => $this->created_at->toDateTimeString(), + ]; + } +} diff --git a/app/Endpoint/Api/Http/Resources/Dealer/DealerDeliveryProductResource.php b/app/Endpoint/Api/Http/Resources/Dealer/DealerDeliveryProductResource.php new file mode 100644 index 00000000..dee14701 --- /dev/null +++ b/app/Endpoint/Api/Http/Resources/Dealer/DealerDeliveryProductResource.php @@ -0,0 +1,24 @@ + $this->product->id, + 'name' => $this->product->name, + 'cover' => $this->product->cover, + 'qty' => $this->qty, + ]; + } +} diff --git a/app/Endpoint/Api/Http/Resources/Dealer/OrderProductResource.php b/app/Endpoint/Api/Http/Resources/Dealer/OrderProductResource.php index 2ede93b9..727db3a9 100644 --- a/app/Endpoint/Api/Http/Resources/Dealer/OrderProductResource.php +++ b/app/Endpoint/Api/Http/Resources/Dealer/OrderProductResource.php @@ -15,11 +15,13 @@ class OrderProductResource extends JsonResource public function toArray($request) { return [ + 'id' => $this->id, 'name' => $this->name, 'cover'=> $this->cover, 'price'=>$this->price, 'sale_price' =>$this->sale_price, 'qty' =>$this->qty, + 'deposit_qty'=> $this->deposit_qty, ]; } } diff --git a/app/Endpoint/Api/Http/Resources/Dealer/UserProductLogResource.php b/app/Endpoint/Api/Http/Resources/Dealer/UserProductLogResource.php index e5d39a51..5d0e44ef 100644 --- a/app/Endpoint/Api/Http/Resources/Dealer/UserProductLogResource.php +++ b/app/Endpoint/Api/Http/Resources/Dealer/UserProductLogResource.php @@ -2,7 +2,6 @@ namespace App\Endpoint\Api\Http\Resources\Dealer; -use App\Models\DealerUserProductLog; use Illuminate\Http\Resources\Json\JsonResource; class UserProductLogResource extends JsonResource @@ -19,11 +18,8 @@ class UserProductLogResource extends JsonResource 'id' => $this->id, 'product_name'=> $this->product?->name, 'remark' => $this->remark, - 'qty' => (in_array($this->type, [ - DealerUserProductLog::TYPE_ORDER_IN, - DealerUserProductLog::TYPE_ADMIN_IN, - DealerUserProductLog::TYPE_REVOKE_IN, - ]) ? '+' : '-').$this->qty.$this->product?->unit, + 'is_deposit' => $this->is_deposit, + 'qty' => $this->qty_format.$this->product?->unit, 'created_at' => $this->created_at->toDateTimeString(), 'can_revoke' => $this->canRevoke(), ]; diff --git a/app/Endpoint/Api/Http/Resources/Dealer/UserProductResource.php b/app/Endpoint/Api/Http/Resources/Dealer/UserProductResource.php index f0a4e8f6..16aa30eb 100644 --- a/app/Endpoint/Api/Http/Resources/Dealer/UserProductResource.php +++ b/app/Endpoint/Api/Http/Resources/Dealer/UserProductResource.php @@ -20,6 +20,7 @@ class UserProductResource extends JsonResource 'cover' => (string) $this->product?->cover, 'price' => (string) $this->product?->price, 'stock' => (int) $this->stock, + 'deposit_stock'=> (int) $this->deposit_stock, ]; } } diff --git a/app/Endpoint/Api/routes.php b/app/Endpoint/Api/routes.php index ee6afac7..0a994d12 100644 --- a/app/Endpoint/Api/routes.php +++ b/app/Endpoint/Api/routes.php @@ -85,6 +85,7 @@ Route::group([ //获取配置 Route::get('configs', [SettingController::class, 'index']); + Route::get('configs-custom', [SettingController::class, 'custom']); //三方登录聚合 Route::group([ @@ -299,5 +300,12 @@ Route::group([ Route::get('purchase-subsidies', [Dealer\PurchaseSubsidyController::class, 'index']); // 进货补贴流水 Route::get('purchase-subsidies/{purchase_subsidy}/logs', [Dealer\PurchaseSubsidyLogController::class, 'index']); + + // 云仓库 + Route::get('delivery-bills', [Dealer\DealerDeliveryBillController::class, 'index']); + Route::post('delivery-bills', [Dealer\DealerDeliveryBillController::class, 'store']); + Route::get('delivery-bills/{delivery_bill}', [Dealer\DealerDeliveryBillController::class, 'show']); + Route::post('delivery-bills/{delivery_bill}/pay', [Dealer\DealerDeliveryBillController::class, 'pay']); + Route::post('delivery-bills/{delivery_bill}/cancel', [Dealer\DealerDeliveryBillController::class, 'cancel']); }); }); diff --git a/app/Enums/DealerDeliveryBillStatus.php b/app/Enums/DealerDeliveryBillStatus.php new file mode 100644 index 00000000..a042d81c --- /dev/null +++ b/app/Enums/DealerDeliveryBillStatus.php @@ -0,0 +1,32 @@ +value]; + } + + public static function colors() + { + return [ + static::Pending->value => '#5b69bc', + static::Paid->value => '#21b978', + static::Cancelled->value => '#b3b9bf', + ]; + } + + public static function texts() + { + return [ + static::Pending->value => '待付款', + static::Paid->value => '已付款', + static::Cancelled->value => '已取消', + ]; + } +} diff --git a/app/Enums/DealerEarningStatus.php b/app/Enums/DealerEarningStatus.php index 8a0e722f..2b8a9b5e 100644 --- a/app/Enums/DealerEarningStatus.php +++ b/app/Enums/DealerEarningStatus.php @@ -7,15 +7,31 @@ enum DealerEarningStatus: int { case Paid = 1;//已打款 case Completed = 5;//已完成 - /** - * @return string - */ + public function color() + { + return static::colors()[$this->value] ?? '#5b69bc'; + } + public function text() { - return match ($this) { - static::Pending => '待打款', - static::Paid => '待收款', - static::Completed => '已完成', - }; + return static::texts()[$this->value]; + } + + public static function colors() + { + return [ + static::Pending->value => '#5b69bc', + static::Paid->value => '#3085d6', + static::Completed->value => '#21b978', + ]; + } + + public static function texts() + { + return [ + static::Pending->value => '待打款', + static::Paid->value => '待收款', + static::Completed->value => '已完成', + ]; } } diff --git a/app/Enums/DealerManageSubsidyStatus.php b/app/Enums/DealerManageSubsidyStatus.php index 3dac0ed1..51367fcc 100644 --- a/app/Enums/DealerManageSubsidyStatus.php +++ b/app/Enums/DealerManageSubsidyStatus.php @@ -6,19 +6,19 @@ enum DealerManageSubsidyStatus: int { case Pending = 0; case Completed = 5; - public function color(): string - { - return match ($this) { - static::Pending => '#5b69bc', - static::Completed => '#21b978', - }; - } - public function text(): string { return static::texts()[$this->value] ?? 'Unknown'; } + public static function colors(): array + { + return [ + static::Pending->value => '#5b69bc', + static::Completed->value => '#21b978', + ]; + } + public static function texts(): array { return [ diff --git a/app/Enums/DealerManagerSubsidyStatus.php b/app/Enums/DealerManagerSubsidyStatus.php index e18c013c..8ee0229a 100644 --- a/app/Enums/DealerManagerSubsidyStatus.php +++ b/app/Enums/DealerManagerSubsidyStatus.php @@ -19,6 +19,14 @@ enum DealerManagerSubsidyStatus: int { return static::texts()[$this->value] ?? 'Unknown'; } + public static function colors(): array + { + return [ + static::Pending->value => '#5b69bc', + static::Completed->value => '#21b978', + ]; + } + public static function texts(): array { return [ diff --git a/app/Enums/DealerPurchaseSubsidyStatus.php b/app/Enums/DealerPurchaseSubsidyStatus.php index d93bab55..fd7987d3 100644 --- a/app/Enums/DealerPurchaseSubsidyStatus.php +++ b/app/Enums/DealerPurchaseSubsidyStatus.php @@ -5,4 +5,25 @@ namespace App\Enums; enum DealerPurchaseSubsidyStatus: int { case Pending = 0; case Completed = 5; + + public function text(): string + { + return static::texts()[$this->value] ?? 'Unknown'; + } + + public static function colors(): array + { + return [ + static::Pending->value => '#5b69bc', + static::Completed->value => '#21b978', + ]; + } + + public static function texts(): array + { + return [ + static::Pending->value => '待付款', + static::Completed->value => '已完成', + ]; + } } diff --git a/app/Enums/DealerWalletAction.php b/app/Enums/DealerWalletAction.php index 6eae5c51..1bacc3a6 100644 --- a/app/Enums/DealerWalletAction.php +++ b/app/Enums/DealerWalletAction.php @@ -15,4 +15,5 @@ enum DealerWalletAction: int { case TransferOut = 10; case EarningIn = 11; case EarningOut = 12; + case DeliveryBillPaid = 13; } diff --git a/app/Enums/PayWay.php b/app/Enums/PayWay.php index d296c6e9..c51f70d5 100644 --- a/app/Enums/PayWay.php +++ b/app/Enums/PayWay.php @@ -37,7 +37,7 @@ enum PayWay: string { }; } - public function getMallOrderText() + public function mallText() { return match ($this) { static::Offline => '线下', @@ -49,25 +49,40 @@ enum PayWay: string { }; } - public function getDealerOrderText() + /** + * 支付方式文本 + * + * @return string + */ + public function text(): string { return match ($this) { - static::Offline => '线下打款', - static::Wallet => '余额支付', - static::WxpayH5 => '微信支付', - default => 'Unknown', + static::Offline => '线下', + static::Balance => '余额', + static::Wallet => '钱包', + static::WxpayApp, static::WxpayH5, static::WxpayJsApi, static::WxpayMiniProgram => '微信支付', + static::AlipayApp => '支付宝', }; } /** - * @return string + * 支付方式对应的颜色 + * + * @return array */ - public static function dealerOrderTexts(): array + public static function colors(): array { return [ - static::Offline->value => '线下打款', - static::Wallet->value => '余额支付', - static::WxpayH5->value => '微信支付', + static::Offline->value => '#5b69bc', + static::Balance->value => '#dda451', + static::Wallet->value => '#ff8acc', + // 微信支付 + static::WxpayApp->value => '#21b978', + static::WxpayH5->value => '#21b978', + static::WxpayJsApi->value => '#21b978', + static::WxpayMiniProgram->value => '#21b978', + // 支付宝 + static::AlipayApp->value => '#3085d6', ]; } } diff --git a/app/Models/Dealer.php b/app/Models/Dealer.php index 72c4e097..5acc6e50 100644 --- a/app/Models/Dealer.php +++ b/app/Models/Dealer.php @@ -355,7 +355,8 @@ class Dealer extends Model public function canWithdraw() { - $days = app_settings('dealer.withdraw_days', 7); - return DealerWalletToBankLog::where('user_id', $this->user_id)->where('created_at', '>', now()->subDays($days))->doesntExist(); + return true; + // $days = app_settings('dealer.withdraw_days', 7); + // return DealerWalletToBankLog::where('user_id', $this->user_id)->where('created_at', '>', now()->subDays($days))->doesntExist(); } } diff --git a/app/Models/DealerDeliveryBill.php b/app/Models/DealerDeliveryBill.php new file mode 100644 index 00000000..39e3c99c --- /dev/null +++ b/app/Models/DealerDeliveryBill.php @@ -0,0 +1,79 @@ + DealerDeliveryBillStatus::Pending, + ]; + + protected $casts = [ + 'status' => DealerDeliveryBillStatus::class, + 'pay_way' => PayWay::class, + 'pay_at' => 'datetime', + ]; + + protected $fillable = [ + 'sn', + 'user_id', + 'order_id', + 'shipping_fee', + 'remark', + 'consignee_name', + 'consignee_telephone', + 'consignee_zone', + 'consignee_address', + 'pay_sn', + 'pay_way', + 'out_trade_no', + 'pay_at', + 'status', + ]; + + /** + * {@inheritdoc} + */ + protected static function booted() + { + parent::creating(function ($bill) { + if ($bill->sn === null) { + do { + $bill->sn = serial_number(); + } while (static::where('sn', $bill->sn)->exists()); + } + }); + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } + + public function order() + { + return $this->hasOne(DealerOrder::class, 'order_id'); + } + + public function payLogs() + { + return $this->morphMany(PayLog::class, 'payable'); + } + + public function deliveryProducts() + { + return $this->hasMany(DealerDeliveryProduct::class, 'delivery_bill_id'); + } + + public function isPending() + { + return $this->status === DealerDeliveryBillStatus::Pending; + } +} diff --git a/app/Models/DealerDeliveryProduct.php b/app/Models/DealerDeliveryProduct.php new file mode 100644 index 00000000..6917bd4e --- /dev/null +++ b/app/Models/DealerDeliveryProduct.php @@ -0,0 +1,13 @@ +belongsTo(DealerProduct::class, 'product_id'); + } +} diff --git a/app/Models/DealerEarning.php b/app/Models/DealerEarning.php index 93765683..b53befe9 100644 --- a/app/Models/DealerEarning.php +++ b/app/Models/DealerEarning.php @@ -68,6 +68,28 @@ class DealerEarning extends Model return $this->morphTo(); } + /** + * 仅查询渠道补贴收益 + * + * @param \Illuminate\Database\Eloquent\Builder $query + * @return \Illuminate\Database\Eloquent\Builder + */ + public function scopeWithoutPayer($query) + { + return $query->whereNull('payer_id'); + } + + /** + * 仅查询渠道补贴收益 + * + * @param \Illuminate\Database\Eloquent\Builder $query + * @return \Illuminate\Database\Eloquent\Builder + */ + public function scopeChannelSubsidy($query) + { + return $query->where('earningable_type', (new DealerChannelSubsidyLog())->getMorphClass()); + } + /** * 待打款 * @@ -92,6 +114,16 @@ class DealerEarning extends Model ]); } + /** + * 确认此收益是否结算 + * + * @return boolean + */ + public function isSettled(): bool + { + return $this->settle_at !== null; + } + /** * 待打款状态 * diff --git a/app/Models/DealerOrder.php b/app/Models/DealerOrder.php index 4d22d0fc..647eba28 100644 --- a/app/Models/DealerOrder.php +++ b/app/Models/DealerOrder.php @@ -51,6 +51,8 @@ class DealerOrder extends Model 'remark', 'pay_sn', 'out_trade_no', + 'local_status', + 'deposit_status', ]; /** @@ -126,6 +128,11 @@ class DealerOrder extends Model return $query->where('status', DealerOrderStatus::Cancelled); } + public function dealerDeliveryBill() + { + return $this->belongsTo(DealerDeliveryBill::class, 'order_id'); + } + /** * 此订单所属的用户的信息 */ @@ -277,6 +284,7 @@ class DealerOrder extends Model { return in_array($this->pay_way, [ PayWay::WxpayH5, + PayWay::WxpayJsApi, ]); } diff --git a/app/Models/DealerOrderProduct.php b/app/Models/DealerOrderProduct.php index 2175e63d..39a009c5 100644 --- a/app/Models/DealerOrderProduct.php +++ b/app/Models/DealerOrderProduct.php @@ -15,6 +15,7 @@ class DealerOrderProduct extends Model 'price', 'sale_price', 'qty', + 'deposit_qty', ]; /** diff --git a/app/Models/DealerPurchaseSubsidy.php b/app/Models/DealerPurchaseSubsidy.php index 3017bd07..bf0b3089 100644 --- a/app/Models/DealerPurchaseSubsidy.php +++ b/app/Models/DealerPurchaseSubsidy.php @@ -49,6 +49,11 @@ class DealerPurchaseSubsidy extends Model return $query->where('settle_state', DealerPurchaseSubsidySettleState::Completed); } + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } + public function dealer() { return $this->belongsTo(Dealer::class, 'user_id', 'user_id'); @@ -64,8 +69,20 @@ class DealerPurchaseSubsidy extends Model return $this->morphOne(DealerEarning::class, 'earningable'); } + /** + * 仅查询待付款的管理津贴 + * + * @return bool + */ + public function isPending(): bool + { + return $this->settle_state === DealerPurchaseSubsidySettleState::Completed + && $this->status === DealerPurchaseSubsidyStatus::Pending; + } + public function isCompleted() { - return $this->status === DealerPurchaseSubsidyStatus::Completed; + return $this->settle_state === DealerPurchaseSubsidySettleState::Completed + && $this->status === DealerPurchaseSubsidyStatus::Completed; } } diff --git a/app/Models/DealerUserProduct.php b/app/Models/DealerUserProduct.php index 65d494a3..b7cb17b5 100644 --- a/app/Models/DealerUserProduct.php +++ b/app/Models/DealerUserProduct.php @@ -11,10 +11,16 @@ class DealerUserProduct extends Model use HasFactory; use HasDateTimeFormatter; + protected $attributes = [ + 'stock' => 0, + 'deposit_stock' => 0, + ]; + protected $fillable = [ 'user_id', 'product_id', 'stock', + 'deposit_stock', ]; public function product() diff --git a/app/Models/DealerUserProductLog.php b/app/Models/DealerUserProductLog.php index 8805dd81..4b869ad0 100644 --- a/app/Models/DealerUserProductLog.php +++ b/app/Models/DealerUserProductLog.php @@ -13,12 +13,27 @@ class DealerUserProductLog extends Model use Filterable; use HasDateTimeFormatter; + // 本地库存 public const TYPE_ORDER_IN = 1; //采购加库存 public const TYPE_ORDER_OUT = 2; //发货扣库存 - public const TYPE_OFFLINE_OUT = 3;//线下去库存 - public const TYPE_ADMIN_IN = 4;//后台添加库存 - public const TYPE_ADMIN_OUT = 5;//后台扣减库存 - public const TYPE_REVOKE_IN = 9;//撤销线下去库存 + public const TYPE_OFFLINE_OUT = 3; //线下去库存 + public const TYPE_ADMIN_IN = 4; //后台添加库存 + public const TYPE_ADMIN_OUT = 5; //后台扣减库存 + public const TYPE_REVOKE_IN = 9; //撤销线下去库存 + public const TYPE_TRANSFER_IN = 6; // 云库存转本地库存 + + // 云库存 + public const TYPE_DEPOSIT_TRANSFER_OUT = 10; // 云库存转本地库存 + public const TYPE_DEPOSIT_TRANSFER_REVOKE = 11; // 撤销云库存转本地库存 + public const TYPE_ORDER_OUT_REVOKE = 12;//撤销发货扣库存 + + protected $attributes = [ + 'is_deposit' => false, + ]; + + protected $casts = [ + 'is_deposit' => 'bool', + ]; protected $fillable = [ 'user_id', @@ -27,6 +42,7 @@ class DealerUserProductLog extends Model 'qty', 'remark', 'revoke_id', + 'is_deposit', ]; public function product() @@ -38,4 +54,18 @@ class DealerUserProductLog extends Model { return ($this->type == static::TYPE_OFFLINE_OUT) && ($this->revoke_id == 0); } + + public function getQtyFormatAttribute() + { + $symbok = in_array($this->type, [ + DealerUserProductLog::TYPE_ORDER_IN, + DealerUserProductLog::TYPE_ADMIN_IN, + DealerUserProductLog::TYPE_REVOKE_IN, + DealerUserProductLog::TYPE_TRANSFER_IN, + DealerUserProductLog::TYPE_DEPOSIT_TRANSFER_REVOKE, + DealerUserProductLog::TYPE_ORDER_OUT_REVOKE, + ]) ? '+' : '-'; + + return $symbok . $this->attributes['qty']; + } } diff --git a/app/Models/Order.php b/app/Models/Order.php index 6521458e..fb703acc 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -417,4 +417,14 @@ class Order extends Model // 其它 return OrderStatus::UNKNOWN; } + + /** + * 获取订单状态 + * + * @return string + */ + public function getOrderStatusTextAttribute(): string + { + return OrderStatus::$statusTexts[$this->order_status] ?? OrderStatus::$statusTexts[OrderStatus::UNKNOWN]; + } } diff --git a/app/Models/PayLog.php b/app/Models/PayLog.php index 8d4bda76..0658395a 100644 --- a/app/Models/PayLog.php +++ b/app/Models/PayLog.php @@ -41,6 +41,20 @@ class PayLog extends Model 'failed_reason', ]; + /** + * {@inheritdoc} + */ + protected static function booted() + { + parent::creating(function ($payLog) { + if ($payLog->pay_sn === null) { + do { + $payLog->pay_sn = serial_number(); + } while (static::where('pay_sn', $payLog->pay_sn)->exists()); + } + }); + } + /** * 获取支付记录所属的模型 */ diff --git a/app/Models/User.php b/app/Models/User.php index f793e61f..4e481af9 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -197,6 +197,15 @@ class User extends Model implements AuthorizableContract, AuthenticatableContrac return $this->hasMany(DealerOrder::class, 'user_id'); } + /** + * 经销商提货单 + * + */ + public function dealerDeliveryBills() + { + return $this->hasMany(DealerDeliveryBill::class, 'user_id'); + } + /** * 经销商的发货订单 * diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index be546720..e7d0002d 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -66,6 +66,7 @@ class AppServiceProvider extends ServiceProvider 'dealer_wallet_to_bank_log' => \App\Models\DealerWalletToBankLog::class, 'dealer_earnings'=> \App\Models\DealerEarning::class, 'dealer_wallet_log'=> \App\Models\DealerWalletLog::class, + 'dealer_delivery_bill'=> \App\Models\DealerDeliveryBill::class, ]); JsonResource::withoutWrapping(); diff --git a/app/Services/Dealer/DealerDeliveryBillService.php b/app/Services/Dealer/DealerDeliveryBillService.php new file mode 100644 index 00000000..dca2a9dc --- /dev/null +++ b/app/Services/Dealer/DealerDeliveryBillService.php @@ -0,0 +1,259 @@ +count()) { + throw new BizException('提货单商品已丢失'); + } + + // 经销商的商品库存 + $dealerProducts = $user->dealerProducts() + ->whereIn('product_id', $deliveryProductIds) + ->get(); + + // 创建提货单 + $deliveryBill = DealerDeliveryBill::create([ + 'user_id' => $user->id, + 'shipping_fee' => app_settings('dealer.delivery_bill_shipping_fee', 0), + 'remark' => $remark, + 'consignee_name' => $consignee['name'], + 'consignee_telephone' => $consignee['telephone'], + 'consignee_zone' => $consignee['zone'], + 'consignee_address' => $consignee['address'], + 'status' => DealerDeliveryBillStatus::Pending, + 'order_id' => $dealerOrder?->id, + ]); + + $mapProducts = $products->keyBy('id'); + $mapDealerProducts = $dealerProducts->keyBy('product_id'); + + $dealerProductLogs = []; + $deliveryBillProducts = []; + + foreach ($deliveryProducts as $deliveryProduct) { + $product = Arr::get($mapProducts, $deliveryProduct['id']); + $dealerProduct = Arr::get($mapDealerProducts, $product->id); + + if ($dealerProduct?->deposit_stock < $deliveryProduct['qty']) { + throw new BizException("{$product->name} 库存不足"); + } + + $dealerProduct->decrement('deposit_stock', $deliveryProduct['qty']); + + $dealerProductLogs[] = [ + 'user_id' => $dealerProduct->user_id, + 'product_id' => $dealerProduct->product_id, + 'type' => DealerUserProductLog::TYPE_DEPOSIT_TRANSFER_OUT, + 'qty' => $deliveryProduct['qty'], + 'remark' => "提货单【{$deliveryBill->sn}】", + 'is_deposit' => true, + 'created_at' => $deliveryBill->created_at, + 'updated_at' => $deliveryBill->updated_at, + ]; + + $deliveryBillProducts[] = [ + 'delivery_bill_id' => $deliveryBill->id, + 'product_id' => $product->id, + 'qty' => $deliveryProduct['qty'], + 'created_at' => $deliveryBill->created_at, + 'updated_at' => $deliveryBill->updated_at, + ]; + } + + DealerUserProductLog::insert($dealerProductLogs); + + DealerDeliveryProduct::insert($deliveryBillProducts); + + return $deliveryBill; + } + + /** + * 提货单付款 + * + * @param \App\Models\DealerDeliveryBill $deliveryBill + * @param \App\Enums\PayWay $payWay + * @return array + * + * @throws \App\Exceptions\BizException + */ + public function pay(DealerDeliveryBill $deliveryBill, PayWay $payWay): array + { + if (! $deliveryBill->isPending()) { + throw new BizException('提货单状态不是待付款'); + } + + $method = match ($payWay) { + PayWay::Wallet => 'payUsingWallet', + PayWay::WxpayH5, PayWay::WxpayJsApi => 'payUsingWxpay', + default => 'payUsingDefault', + }; + + if (! method_exists($this, $method)) { + throw new BizException('交易非法'); + } + + $payLog = $deliveryBill->payLogs()->create([ + 'pay_way' => $payWay, + ]); + + return [ + 'pay_way' => $payLog->pay_way, + 'data' => $this->{$method}($deliveryBill, $payLog), + ]; + } + + /** + * 取消提货单 + * + * @param DealerDeliveryBill $deliveryBill + * @return void + */ + public function cancel(DealerDeliveryBill $deliveryBill) + { + if (! $deliveryBill->isPending()) { + throw new BizException('提货单状态不是待付款'); + } + + $deliveryBill->update([ + 'status' => DealerDeliveryBillStatus::Cancelled, + ]); + + $dealerProductLogs = []; + + foreach ($deliveryBill->deliveryProducts as $deliveryProduct) { + DealerUserProduct::where([ + 'user_id' => $deliveryBill->user_id, + 'product_id' => $deliveryProduct->product_id, + ])->update([ + 'deposit_stock' => DB::raw("deposit_stock + {$deliveryProduct->qty}"), + ]); + + $dealerProductLogs[] = [ + 'user_id' => $deliveryBill->user_id, + 'product_id' => $deliveryProduct->product_id, + 'type' => DealerUserProductLog::TYPE_DEPOSIT_TRANSFER_REVOKE, + 'qty' => $deliveryProduct->qty, + 'remark' => "撤销提货单【{$deliveryBill->sn}】", + 'is_deposit' => true, + 'created_at' => $deliveryBill->updated_at, + 'updated_at' => $deliveryBill->updated_at, + ]; + } + + //撤回已发货的本地库存 + if ($deliveryBill->order_id) { + $dealerOrder = DealerOrder::with('products')->find($deliveryBill->order_id); + foreach ($dealerOrder->products as $product) { + if ($product->qty > 0 && $dealerOrder->local_status == 1) { + $userProduct = $dealerOrder->consignor->dealerProducts()->firstOrCreate([ + 'product_id'=> $product->product_id, + ]); + $userProduct->increment('stock', $product->qty); + $dealerProductLogs[] = [ + 'user_id' => $dealerOrder->consignor_id, + 'product_id' => $product->product_id, + 'type' => DealerUserProductLog::TYPE_ORDER_OUT_REVOKE, + 'qty' => $product->qty, + 'remark' => "撤销订单发货【{$dealerOrder->sn}】", + 'is_deposit' => false, + 'created_at' => $deliveryBill->updated_at, + 'updated_at' => $deliveryBill->updated_at, + ]; + } + //将云仓发货的数量挪回来 + DealerOrderProduct::where('id', $product->id)->update([ + 'qty' => $product->qty + $product->deposit_qty, + 'deposit_qty' => 0, + ]); + } + $dealerOrder->update([ + 'local_status'=>0, + ]); + } + + DealerUserProductLog::insert($dealerProductLogs); + } + + /** + * 使用余额支付 + * + * @param \App\Models\DealerDeliveryBill $deliveryBill + * @param \App\Models\PayLog $payLog + * @return void + */ + protected function payUsingWallet(DealerDeliveryBill $deliveryBill, PayLog $payLog) + { + (new WalletService())->changeBalance( + $deliveryBill->user, + bcmul($deliveryBill->shipping_fee, '-1', 2), + DealerWalletAction::DeliveryBillPaid, + "提货单号:{$deliveryBill->sn}", + $deliveryBill + ); + + (new PayService())->handleSuccess($payLog, [ + 'pay_at' => now(), + ]); + } + + /** + * 使用微信支付 + * + * @param \App\Models\DealerDeliveryBill $deliveryBill + * @param \App\Models\PayLog $payLog + * @return array + */ + protected function payUsingWxpay(DealerDeliveryBill $deliveryBill, PayLog $payLog): array + { + if (is_null($tradeType = WxpayTradeType::tryFromPayWay($payLog->pay_way))) { + throw new BizException('支付方式 非法'); + } + + $params = [ + 'body' => app_settings('app.app_name').'-批零订单', + 'out_trade_no' => $payLog->pay_sn, + 'total_fee' => bcmul($deliveryBill->shipping_fee, '100'), + 'trade_type' => $tradeType->value, + ]; + + return (new WxpayService())->pay($params, 'yzk_h5'); + } +} diff --git a/app/Services/Dealer/ManageSubsidyService.php b/app/Services/Dealer/ManageSubsidyService.php deleted file mode 100644 index 24a50bdd..00000000 --- a/app/Services/Dealer/ManageSubsidyService.php +++ /dev/null @@ -1,43 +0,0 @@ -isPending()) { - throw new BizException('管理津贴 不是待付款状态'); - } - - $dealerManageSubsidy->update([ - 'status' => DealerManageSubsidyStatus::Completed, - ]); - - $dealerManageSubsidy->earning->update([ - 'pay_way' => DealerEarning::PAY_WAY_WALLET, - 'pay_at' => now(), - 'pay_info' => null, - 'status' => DealerEarningStatus::Completed, - ]); - - (new WalletService())->changeBalance( - $dealerManageSubsidy->user, - $dealerManageSubsidy->real_amount, - DealerWalletAction::ManageSubsidyIn, - '收入-管理津贴', - $dealerManageSubsidy - ); - } -} diff --git a/app/Services/Dealer/ManagerSubsidyService.php b/app/Services/Dealer/ManagerSubsidyService.php deleted file mode 100644 index ab766710..00000000 --- a/app/Services/Dealer/ManagerSubsidyService.php +++ /dev/null @@ -1,43 +0,0 @@ -isPending()) { - throw new BizException('管理者津贴 不是待付款状态'); - } - - $dealerManagerSubsidy->update([ - 'status' => DealerManagerSubsidyStatus::Completed, - ]); - - $dealerManagerSubsidy->earning->update([ - 'pay_way' => DealerEarning::PAY_WAY_WALLET, - 'pay_at' => now(), - 'pay_info' => null, - 'status' => DealerEarningStatus::Completed, - ]); - - (new WalletService())->changeBalance( - $dealerManagerSubsidy->user, - $dealerManagerSubsidy->real_amount, - DealerWalletAction::ManagerSubsidyIn, - '收入-管理者津贴', - $dealerManagerSubsidy - ); - } -} diff --git a/app/Services/Dealer/OrderService.php b/app/Services/Dealer/OrderService.php index 558b586e..1dbf30ea 100644 --- a/app/Services/Dealer/OrderService.php +++ b/app/Services/Dealer/OrderService.php @@ -2,16 +2,19 @@ namespace App\Services\Dealer; +use App\Enums\DealerDeliveryBillStatus; use App\Enums\DealerLvl; use App\Enums\DealerOrderStatus; use App\Enums\DealerWalletAction; use App\Enums\PayWay; use App\Enums\WxpayTradeType; use App\Exceptions\BizException; +use App\Models\DealerDeliveryBill; use App\Models\DealerOrder; use App\Models\DealerOrderAllocateLog; use App\Models\DealerOrderProduct; use App\Models\DealerProduct; +use App\Models\DealerUserProduct; use App\Models\DealerUserProductLog; use App\Models\ShippingAddress; use App\Models\User; @@ -283,20 +286,9 @@ class OrderService throw new BizException('订单状态不是待付款'); } - do { - $payLog = null; - - try { - $payLog = $order->payLogs()->create([ - 'pay_sn' => serial_number(), - 'pay_way' => $payWay, - ]); - } catch (QueryException $e) { - if (strpos($e->getMessage(), 'Duplicate entry') === false) { - throw $e; - } - } - } while ($payLog === null); + $payLog = $order->payLogs()->create([ + 'pay_way' => $payWay, + ]); $data = [ 'pay_sn' => $payLog->pay_sn, @@ -346,6 +338,7 @@ class OrderService break; case PayWay::WxpayH5: + case PayWay::WxpayJsApi: if (is_null($tradeType = WxpayTradeType::tryFromPayWay($payLog->pay_way))) { throw new BizException('支付方式 非法'); } @@ -387,6 +380,10 @@ class OrderService 'status' => DealerOrderStatus::Paid, 'paied_time' => now(), ]); + //签约单,云库存直接发货 + if ($order->consignor === null) { + $this->orderInDepositstock($order); + } return $order; } @@ -394,16 +391,21 @@ class OrderService * 确认发货 * * @param DealerOrder $order + * @param string $action * @return DealerOrder $order */ - public function shippingOrder(DealerOrder $order) + public function shippingOrder(DealerOrder $order, ?string $action = 'qty') { if (!$order->isPaid()) { throw new BizException('无法发货:订单状态异常,请刷新后再试'); } //扣减发货人库存 if ($order->consignor) { - $this->orderOutQty($order); + if ($action == 'deposit_qty') { + $this->orderOutDepositQty($order); + } else { + $this->orderOutQty($order); + } } $order->update([ @@ -413,7 +415,67 @@ class OrderService return $order; } - public function shippingedOrder(DealerOrder $order) + /** + * 使用云仓发货 + */ + public function shippingOrderByDeposit(DealerOrder $order) + { + $depositProducts = []; + $deliveryBill = null; + //判断这个订单是否已经有待支付的云仓发货单 + if ($deliveryBill = DealerDeliveryBill::where([ + 'user_id' => $order->consignor_id, + 'order_id' => $order->id, + 'status' => DealerDeliveryBillStatus::Pending, + ])->first()) { + return $deliveryBill; + } + //判断本地库存是否足够,不足则生成云仓提货单,并唤起支付。 + foreach ($order->products as $product) { + //记录需要生成云仓发货单的商品信息以及数量 + if ($_userProduct = DealerUserProduct::where([ + 'user_id'=>$order->consignor_id, + 'product_id'=>$product->product_id, + ])->first()) { + if ($product->qty > $_userProduct->stock) { + if ($product->qty > $_userProduct->stock + $_userProduct->deposit_stock) { + throw new BizException('当前可发货库存不足'); + } + //记录 + $depositProducts[$product->id] = [ + 'id'=> $product->product_id, + 'qty'=> $product->qty - $_userProduct->stock, + ]; + } + } else { + throw new BizException('当前可发货库存不足'); + } + } + + if ($depositProducts) { + $dealerDeliveryBillService = new DealerDeliveryBillService(); + $deliveryBill = $dealerDeliveryBillService->create($order->consignor, $depositProducts, [ + 'name'=>$order->consignee_name, + 'telephone'=>$order->consignee_telephone, + 'zone'=>$order->consignee_zone, + 'address'=>$order->consignee_address, + ], '订单发货:【'.$order->sn.'】', $order); + //更新订单相关的库存发货情况 + if ($order) { + foreach ($order->products as $product) { + if (isset($depositProducts[$product->id])) { + DealerOrderProduct::where('id', $product->id)->update([ + 'qty' => $product->qty - $depositProducts[$product->id]['qty'], + 'deposit_qty' => $depositProducts[$product->id]['qty'], + ]); + } + } + } + } + return $deliveryBill; + } + + public function shippingedOrder(DealerOrder $order, ?string $action = 'qty') { if (!$order->isShipping()) { throw new BizException('无法收货:订单状态异常,请刷新后再试'); @@ -490,42 +552,145 @@ class OrderService protected function orderInQty(DealerOrder $order) { foreach ($order->products as $product) { + //增加本地库存 $userProduct = $order->user->dealerProducts()->firstOrCreate([ 'product_id'=> $product->product_id, ]); - $userProduct->increment('stock', $product->qty); + //如果云仓已收货 + if ($order->local_status < 2 && $order->deposit_status !== 1) { + $userProduct->increment('stock', $product->qty); + + DealerUserProductLog::create([ + 'user_id'=> $order->user_id, + 'product_id'=> $product->product_id, + 'type' => DealerUserProductLog::TYPE_ORDER_IN, + 'qty'=>$product->qty, + 'remark'=>'订单:'.$order->sn, + ]); + } else { + $userProduct->increment('stock', $product->qty + $product->deposit_qty); + + DealerUserProductLog::create([ + 'user_id'=> $order->user_id, + 'product_id'=> $product->product_id, + 'type' => DealerUserProductLog::TYPE_ORDER_IN, + 'qty'=>$product->qty + $product->deposit_qty, + 'remark'=>'订单:'.$order->sn, + ]); + } + } + $order->update([ + 'local_status' => 2, + 'deposit_status'=> 2, + ]); + } + + /** + * 用户通过订单云仓库增加本地库存 + * + * @return void + */ + protected function orderInAllQty(DealerOrder $order) + { + foreach ($order->products as $product) { + $userProduct = $order->user->dealerProducts()->firstOrCreate([ + 'product_id'=> $product->product_id, + ]); + $userProduct->increment('stock', $product->qty + $product->deposit_qty); DealerUserProductLog::create([ 'user_id'=> $order->user_id, 'product_id'=> $product->product_id, 'type' => DealerUserProductLog::TYPE_ORDER_IN, - 'qty'=>$product->qty, + 'qty'=>$product->qty + $product->deposit_qty, 'remark'=>'订单:'.$order->sn, ]); } } /** - * 用户通过订单扣减库存 + * 用户通过订单扣减本地库存发货 * * @return void */ - protected function orderOutQty(DealerOrder $order) + public function orderOutQty(DealerOrder $order) { foreach ($order->products as $product) { - $userProduct = $order->consignor->dealerProducts()->firstOrCreate([ - 'product_id'=> $product->product_id, - ]); - $userProduct->decrement('stock', $product->qty); - - DealerUserProductLog::create([ - 'user_id'=> $order->consignor_id, - 'product_id'=> $product->product_id, - 'type' => DealerUserProductLog::TYPE_ORDER_OUT, - 'qty'=>$product->qty, - 'remark' =>'订单:'.$order->sn, - ]); + if ($product->qty > 0 && $order->local_status == 0) { + $userProduct = $order->consignor->dealerProducts()->firstOrCreate([ + 'product_id'=> $product->product_id, + ]); + $userProduct->decrement('stock', $product->qty); + DealerUserProductLog::create([ + 'user_id'=> $order->consignor_id, + 'product_id'=> $product->product_id, + 'type' => DealerUserProductLog::TYPE_ORDER_OUT, + 'qty'=>$product->qty, + 'remark' =>'订单:'.$order->sn, + ]); + } } + $order->update([ + 'local_status'=>1, + ]); + } + + /** + * 用户通过订单扣减云库存发货 + * + * @return void + */ + public function orderOutDepositQty(DealerOrder $order) + { + foreach ($order->products as $product) { + if ($product->deposit_qty > 0 && $order->deposit_status == 0) { + $userProduct = $order->consignor->dealerProducts()->firstOrCreate([ + 'product_id'=> $product->product_id, + ]); + $userProduct->decrement('stock', $product->deposit_qty); + + DealerUserProductLog::create([ + 'user_id'=> $order->consignor_id, + 'product_id'=> $product->product_id, + 'type' => DealerUserProductLog::TYPE_ORDER_OUT, + 'qty'=>$product->deposit_qty, + 'remark' =>'订单:'.$order->sn, + ]); + } + } + $order->update([ + 'deposit_status'=>1, + ]); + } + + /** + * 用户通过订单获取云库存 + * + * @param DealerOrder $order + * @return void + */ + public function orderInDepositstock(DealerOrder $order) + { + foreach ($order->products as $product) { + if ($product->deposit_qty > 0 && $order->deposit_status == 0) { + $userProduct = $order->user->dealerProducts()->firstOrCreate([ + 'product_id'=> $product->product_id, + ]); + $userProduct->increment('deposit_stock', $product->deposit_qty); + + DealerUserProductLog::create([ + 'is_deposit'=>true, + 'user_id'=> $order->user_id, + 'product_id'=> $product->product_id, + 'type' => DealerUserProductLog::TYPE_ORDER_IN, + 'qty'=>$product->deposit_qty, + 'remark' =>'订单:'.$order->sn, + ]); + } + } + $order->update([ + 'deposit_status'=>2, + ]); } private function getConsignor(User $user, $totalAmount, ?User $lastConsignor = null) diff --git a/app/Services/PayService.php b/app/Services/PayService.php index fb7e4183..9de2c686 100644 --- a/app/Services/PayService.php +++ b/app/Services/PayService.php @@ -2,13 +2,19 @@ namespace App\Services; +use App\Enums\DealerDeliveryBillStatus; use App\Enums\DealerOrderStatus; use App\Exceptions\BizException; use App\Exceptions\InvalidPaySerialNumberException; +use App\Models\DealerDeliveryBill; use App\Models\DealerOrder; +use App\Models\DealerUserProduct; +use App\Models\DealerUserProductLog; use App\Models\DistributionPreIncomeJob; use App\Models\Order; use App\Models\PayLog; +use App\Services\Dealer\OrderService; +use Illuminate\Support\Facades\DB; class PayService { @@ -99,9 +105,55 @@ class PayService } else { $payable->paied_time = $payLog->pay_at; $payable->status = DealerOrderStatus::Paid; + //签约单,云库存直接发货 + if ($payable->consignor === null) { + (new OrderService())->orderInDepositstock($payable); + } } $payable->save(); + } elseif ($payable instanceof DealerDeliveryBill) { + if (! $payable->isPending()) { + throw new BizException('提货单状态不是待打款'); + } + + $payable->pay_sn = $payLog->pay_sn; + $payable->pay_at = $payLog->pay_at; + $payable->out_trade_no = $payLog->out_trade_no; + $payable->pay_way = $payLog->pay_way; + $payable->status = DealerDeliveryBillStatus::Paid; + $payable->save(); + + // 将云仓库存变更为本地库存 + $dealerProductLogs = []; + + foreach ($payable->deliveryProducts as $deliveryProduct) { + DealerUserProduct::where([ + 'user_id' => $payable->user_id, + 'product_id' => $deliveryProduct->product_id, + ])->update([ + 'stock' => DB::raw("stock + {$deliveryProduct->qty}"), + ]); + + $dealerProductLogs[] = [ + 'user_id' => $payable->user_id, + 'product_id' => $deliveryProduct->product_id, + 'type' => DealerUserProductLog::TYPE_TRANSFER_IN, + 'qty' => $deliveryProduct->qty, + 'remark' => "云库存转本地库存,提货单【{$payable->sn}】", + 'is_deposit' => false, + 'created_at' => $payable->updated_at, + 'updated_at' => $payable->updated_at, + ]; + } + + DealerUserProductLog::insert($dealerProductLogs); + + //如果云仓库提货单存在关联订单,则该订单直接处理云仓部分的发货 + if ($payable->order_id) { + $dealerOrder = DealerOrder::find($payable->order_id); + (new OrderService())->shippingOrder($dealerOrder, 'deposit_qty'); + } } return $payLog; diff --git a/database/migrations/2022_03_08_100618_add_deposit_stock_to_dealer_user_products_table.php b/database/migrations/2022_03_08_100618_add_deposit_stock_to_dealer_user_products_table.php new file mode 100644 index 00000000..ad487abc --- /dev/null +++ b/database/migrations/2022_03_08_100618_add_deposit_stock_to_dealer_user_products_table.php @@ -0,0 +1,32 @@ +unsignedBigInteger('deposit_stock')->default(0)->comment('托管库存'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('dealer_user_products', function (Blueprint $table) { + $table->dropColumn(['deposit_stock']); + }); + } +} diff --git a/database/migrations/2022_03_08_103158_add_deposit_qty_to_dealer_order_products_table.php b/database/migrations/2022_03_08_103158_add_deposit_qty_to_dealer_order_products_table.php new file mode 100644 index 00000000..5c669c25 --- /dev/null +++ b/database/migrations/2022_03_08_103158_add_deposit_qty_to_dealer_order_products_table.php @@ -0,0 +1,32 @@ +unsignedBigInteger('deposit_qty')->default(0)->comment('托管数量'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('dealer_order_products', function (Blueprint $table) { + $table->dropColumn(['deposit_qty']); + }); + } +} diff --git a/database/migrations/2022_03_08_105741_create_dealer_delivery_bills_table.php b/database/migrations/2022_03_08_105741_create_dealer_delivery_bills_table.php new file mode 100644 index 00000000..bfea05d8 --- /dev/null +++ b/database/migrations/2022_03_08_105741_create_dealer_delivery_bills_table.php @@ -0,0 +1,50 @@ +id(); + $table->string('sn')->unique(); + $table->unsignedBigInteger('user_id'); + $table->unsignedBigInteger('order_id')->nullable(); + $table->unsignedDecimal('shipping_fee', 18, 2)->default(0)->comment('运费'); + $table->string('remark')->nullable()->comment('备注'); + + // 收货人信息 + $table->string('consignee_name')->nullable()->comment('收货人-姓名'); + $table->string('consignee_telephone')->nullable()->comment('收货人-联系方式'); + $table->string('consignee_zone')->nullable()->comment('收货人-所在地区'); + $table->string('consignee_address')->nullable()->comment('收货人-详细地址'); + + // 支付信息 + $table->string('pay_sn')->nullable()->comment('支付单号'); + $table->string('pay_way')->nullable()->comment('支付方式'); + $table->string('out_trade_no')->nullable()->comment('外部交易号'); + $table->timestamp('pay_at')->nullable()->comment('付款时间'); + + $table->tinyInteger('status')->default(0)->comment('状态'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('dealer_delivery_bills'); + } +} diff --git a/database/migrations/2022_03_08_105753_create_dealer_delivery_products_table.php b/database/migrations/2022_03_08_105753_create_dealer_delivery_products_table.php new file mode 100644 index 00000000..13f901a6 --- /dev/null +++ b/database/migrations/2022_03_08_105753_create_dealer_delivery_products_table.php @@ -0,0 +1,36 @@ +id(); + $table->unsignedBigInteger('delivery_bill_id'); + $table->unsignedBigInteger('product_id'); + $table->unsignedBigInteger('qty'); + $table->timestamps(); + + $table->unique(['delivery_bill_id', 'product_id']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('dealer_delivery_products'); + } +} diff --git a/database/migrations/2022_03_08_145732_add_is_deposit_to_dealer_user_product_logs_table.php b/database/migrations/2022_03_08_145732_add_is_deposit_to_dealer_user_product_logs_table.php new file mode 100644 index 00000000..387a7162 --- /dev/null +++ b/database/migrations/2022_03_08_145732_add_is_deposit_to_dealer_user_product_logs_table.php @@ -0,0 +1,34 @@ +boolean('is_deposit')->default(false)->comment('是否云库存'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('dealer_user_product_logs', function (Blueprint $table) { + // + $table->dropColumn(['is_deposit']); + }); + } +} diff --git a/database/migrations/2022_03_14_113838_add_deposit_status_to_dealer_orders_table.php b/database/migrations/2022_03_14_113838_add_deposit_status_to_dealer_orders_table.php new file mode 100644 index 00000000..6dce48aa --- /dev/null +++ b/database/migrations/2022_03_14_113838_add_deposit_status_to_dealer_orders_table.php @@ -0,0 +1,35 @@ +unsignedTinyInteger('local_status')->nullable()->default(0)->comment('本地仓发货状态'); + $table->unsignedTinyInteger('deposit_status')->nullable()->default(0)->comment('云仓发货状态'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('dealer_orders', function (Blueprint $table) { + // + $table->dropColumn(['local_status', 'deposit_status']); + }); + } +} diff --git a/database/seeders/AdminMenuSeeder.php b/database/seeders/AdminMenuSeeder.php index 3b2a3654..dc141fb4 100644 --- a/database/seeders/AdminMenuSeeder.php +++ b/database/seeders/AdminMenuSeeder.php @@ -309,6 +309,11 @@ class AdminMenuSeeder extends Seeder 'icon' => '', 'uri' => 'dealer-manager-orders?type=manager&filter-status[]=2&filter-status[]=3', ], + [ + 'title' => '云仓提货单', + 'icon' => '', + 'uri' => 'dealer-delivery-bills', + ], // [ // 'title' =>'用户资金', // 'icon'=>'', @@ -319,10 +324,16 @@ class AdminMenuSeeder extends Seeder 'icon'=>'', 'uri' => 'dealer-earnings-channel?filter-earningable_type[]=dealer_channel_subsidy_log', ], + // [ + // 'title' =>'签约渠道补贴', + // 'icon'=>'', + // 'uri' => 'dealer-channel-subsidies', + // ], [ 'title' =>'进货补贴', 'icon' => '', 'uri' => 'dealer-earnings-purchase?filter-earningable_type[]=dealer_purchase_subsidy', + // 'uri' => 'dealer-purchase-subsidies', ], [ 'title' =>'进货补贴明细', @@ -333,6 +344,7 @@ class AdminMenuSeeder extends Seeder 'title'=>'管理津贴', 'icon' => '', 'uri' => 'dealer-earnings-manage?filter-earningable_type[]=dealer_manage_subsidy', + // 'uri' => 'dealer-manage-subsidies', ], [ 'title' =>'管理津贴明细', @@ -343,6 +355,7 @@ class AdminMenuSeeder extends Seeder 'title'=>'管理者津贴', 'icon' => '', 'uri' => 'dealer-earnings-manage?filter-earningable_type[]=dealer_manager_subsidy', + // 'uri' => 'dealer-manager-subsidies', ], [ 'title'=>'管理者津贴明细', diff --git a/database/seeders/AdminPermissionSeeder.php b/database/seeders/AdminPermissionSeeder.php index 6589ac4c..c52c47c2 100644 --- a/database/seeders/AdminPermissionSeeder.php +++ b/database/seeders/AdminPermissionSeeder.php @@ -213,6 +213,7 @@ class AdminPermissionSeeder extends Seeder 'curd' => ['index', 'show'], 'children'=>[ 'export_shipping_orders'=>['name' =>'导出发货单'], + 'export_order_products'=>['name' =>'导出商品'], 'tags'=>['name' =>'标签设置'], 'pay'=>['name' =>'支付订单'], 'reduce'=>['name' =>'订单改价'], @@ -341,6 +342,11 @@ class AdminPermissionSeeder extends Seeder 'cancel'=>['name' =>'取消订单'], ], ], + 'dealer_delivery_bills' => [ + 'name' =>'云仓提货单', + 'curd' => ['index', 'show'], + 'children' =>[], + ], 'dealer_earnings'=>[ 'name' =>'资金管理', 'curd' => ['index', 'show'], @@ -352,6 +358,22 @@ class AdminPermissionSeeder extends Seeder 'pay'=>['name' =>'确认打款'], ], ], + 'dealer_channel_subsidies' => [ + 'name' =>'签约渠道补贴', + 'curd' => ['index'], + 'children' => [ + 'pay' => ['name' => '付款'], + 'batch_pay' => ['name' => '批量付款'], + ], + ], + 'dealer_purchase_subsidies' => [ + 'name' =>'进货补贴', + 'curd' => ['index', 'show'], + 'children' => [ + 'pay' => ['name' => '付款'], + 'batch_pay' => ['name' => '批量付款'], + ], + ], 'dealer_manager_subsidies' => [ 'name' =>'管理者津贴', 'curd' => ['index'],