From aa3ee8acff8a74674b231600917af34df72ea852 Mon Sep 17 00:00:00 2001 From: mzl Date: Mon, 22 Sep 2025 18:49:18 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=A4=87=E8=B4=A7=E5=8D=95&&?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=87=87=E8=B4=AD=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/common/service/PurchaseOrdersService.php | 6 +- app/common/service/StockOrdersService.php | 11 +- app/crm/service/StockOrdersService.php | 678 +---------------- app/crm/service/StockOrdersService_bak2.php | 762 +++++++++++++++++++ 4 files changed, 776 insertions(+), 681 deletions(-) create mode 100644 app/crm/service/StockOrdersService_bak2.php diff --git a/app/common/service/PurchaseOrdersService.php b/app/common/service/PurchaseOrdersService.php index bf4800b..40155f8 100644 --- a/app/common/service/PurchaseOrdersService.php +++ b/app/common/service/PurchaseOrdersService.php @@ -118,7 +118,9 @@ class PurchaseOrdersService $config = $this->buildQueryConfig($request, $scene); // 更新主表信息 - $updated = $this->logicUpdateBy($config, $data); + $udata = $data; + unset($udata['stock_order']); + $updated = $this->logicUpdateBy($config, $udata); // 获取更新后的数据 $order = $this->logicShow($config); @@ -198,7 +200,7 @@ class PurchaseOrdersService } // 删除采购单主表 - $deleted = $this->logicDestroy($config); + $deleted = $this->logicDeleteBy($config); return $this->postProcess('Destroy', $deleted); }); diff --git a/app/common/service/StockOrdersService.php b/app/common/service/StockOrdersService.php index 3ce89d4..6880b6d 100644 --- a/app/common/service/StockOrdersService.php +++ b/app/common/service/StockOrdersService.php @@ -620,7 +620,10 @@ class StockOrdersService 'fob_transport_fee' => 'FOB运输费', 'port_misc_fee' => '港杂', 'retrofit_fee' => '改装费', - 'ocean_freight' => '海运费' + 'ocean_freight' => '海运费', + 'commission' => '佣金', + 'purchase_tax' => '购置税', + 'tax_refund' => '预估退税' ]; foreach ($priceFields as $field => $description) { @@ -703,8 +706,12 @@ class StockOrdersService $portMiscFee = (float)($veh['port_misc_fee']['amount'] ?? 0); $retrofitFee = (float)($veh['retrofit_fee']['amount'] ?? 0); $oceanFreight = (float)($veh['ocean_freight']['amount'] ?? 0); + $commission = (float)($veh['commission']['amount'] ?? 0); + $purchaseTax = (float)($veh['purchase_tax']['amount'] ?? 0); + $taxRefund = (float)($veh['tax_refund']['amount'] ?? 0); - return $unitPrice - $ttPrice - $ddPrice - $handlingFee - $fobTransportFee - $portMiscFee - $retrofitFee - $oceanFreight; + // 退税额 tax_refund 是增加利润的,所以用加法 + return $unitPrice - $ttPrice - $ddPrice - $handlingFee - $fobTransportFee - $portMiscFee - $retrofitFee - $oceanFreight - $commission - $purchaseTax + $taxRefund; } /** diff --git a/app/crm/service/StockOrdersService.php b/app/crm/service/StockOrdersService.php index facc786..dd7d570 100644 --- a/app/crm/service/StockOrdersService.php +++ b/app/crm/service/StockOrdersService.php @@ -2,45 +2,12 @@ namespace app\crm\service; -use app\common\model\StockOrderAccounts; -use app\common\model\StockOrderItemOptions; -use app\common\model\StockOrderItems; -use app\common\validate\StockOrdersValidate; -use app\common\traits\crud\CrudServiceTrait; -use app\common\model\StockOrders; -use app\common\model\SysCurrency; use support\Request; -use app\common\model\User; -use support\Db; -use support\Model; -use app\common\model\ApprovalInstances; -use app\common\model\ApprovalRecords; use support\exception\BusinessException; -class StockOrdersService +class StockOrdersService extends \app\common\service\StockOrdersService { - use CrudServiceTrait; - - /** - * 指定当前要操作的模型 - */ - protected string $modelClass = StockOrders::class; - - /** - * 验证器 - */ - protected $validator = StockOrdersValidate::class; - - /** - * 获取验证器 - */ - public function getValidator() - { - return new $this->validator; - } - - protected function buildQueryConfig(Request $request, string $scene = 'index'): array { $p = $request->all(); @@ -97,302 +64,6 @@ class StockOrdersService return $config; } - public function beforeServiceStore($data, Request $request) - { - $userId = $request->user->uid; - - $data['order_no'] = generateStockNo(); - $data['biz_date'] = strtotime($data['biz_date']); - $data['eta_ship_date'] = strtotime($data['eta_ship_date']); - $data['latest_arrival_date'] = strtotime($data['latest_arrival_date']); - $data['salesperson_id'] = $userId; - $data['salesperson_snapshot'] = User::where('id', $userId)->value('name') ?? ''; - $data['approval_status'] = $data['approval_status'] == 1 ? 1 : 0; - - // if ($data['is_final'] == 1) { - $data['pick_up_date'] = $data['pick_up_date'] ? strtotime($data['pick_up_date']) : 0; - $data['collect_all_date'] = $data['collect_all_date'] ? strtotime($data['collect_all_date']) : 0; - // } - return $data; - } - - protected function afterServiceStore($res) - { - // 处理原单 - if ($res->is_final == 1 && $res->object_id > 0) { - $order = StockOrders::find($res->object_id); - $order->is_use = 1; - $order->object_id = $res->id; - if (!$order->save()) { - flg('备货单【'.$order->order_no.'】创建终版成功,但更新原单失败'); - } - } - // 启动审批 - if ($res->approval_status == 1) { - $this->startApproval($res); - } - return $res; - } - - /** - * 启动审批 - */ - public function startApproval($item) - { - // 启动审批 - $appro = new \app\common\utils\approval\service\ApprovalFlowService(); - $appro->start('stock_orders', 'stock_orders', $item->id, [], $item->salesperson_id); - // 异步生成pdf文件 - \Webman\RedisQueue\Redis::send('stock-order-pdf', ['id'=>$item->id]); - } - - /** - * 创建PDF文件 - */ - public function createdPdfFile($id) - { - $config = []; - $config['model'] = $this->modelClass; - $config['where']['id'] = $id; - $config['with'][] = 'items'; - $config['with'][] = 'items.options'; - $config['with'][] = 'items.moneyValues'; - $config['with'][] = 'accounts'; - $config['with'][] = 'moneyValues'; - $config['with'][] = 'salesperson'; - $item = $this->logicShow($config); - $pdfData = $this->pdfData($item); - - // 渲染模板为最终HTML - $tplPath = app_path('common/utils/pdf/template/stockOrder.stub'); - $html = \app\common\utils\pdf\service\PdfHelper::render($tplPath, ['data' => $pdfData]); - - // 生成PDF - $driver = new \app\common\utils\pdf\service\MpdfDriver(); - $pdfBin = $driver->htmlToPdf($html); - - // 存储 - $dir = public_path('pdf').'/'; - $fileName = ($pdfData['order_no'] ?? date('YmdHis')) . '.pdf'; - - // 本地保存 - \app\common\utils\pdf\service\PdfStorage::saveBinary( - $pdfBin, - $dir, - $fileName - ); - - // 上传云端 - $param = [ - 'path' => $dir . $fileName, - 'scene' => 'stock_orders', - 'category' => 'pdf', - 'original_name' => $fileName - ]; - $up = new \app\common\service\UploadService(); - $res = $up->uploadLocalFile($param); - - @unlink($dir . $fileName); - - // 更新文件地址 - $item->file_url = $res['file_name']; - $item->save(); - - return $res; - } - - public function pdfData($item) - { - $vehList = []; - $index = 0; - $totalProfit = 0; // 总利润 - - // $priceFields = [ - // 'official_price', // '官方指导价', - // 'discount_price', // '优惠', - // 'listing_price', // '二手挂牌价价', - // 'reduction_price', // '优惠', - - // 'handling_fee', // '手续费', - // 'retrofit_fee', // '改装费', - // 'fob_transport_fee', // 'FOB运输费', - // 'port_misc_fee', // '港杂', - // 'ocean_freight', // '海运费', - - // 'unit_price', // '单价', - // 'total_price', // '总价', - // // 'profit', // '利润' - // ]; - - foreach ($item->items as $k => $v) { - $currencyInfo = SysCurrency::where('currency_code', $item->settlement_currency)->first(['currency_symbol', 'currency_name'])->toArray(); - - // 存在则数量加1 - if (count($vehList) > 0 && isset($vehList[$v->unique_id])) { - $vehList[$v->unique_id]['num'] += 1; - $vehList[$v->unique_id]['total_price']['currency_info'] = $currencyInfo['currency_symbol'].number_format($vehList[$v->unique_id]['total_price']['amount']*$vehList[$v->unique_id]['num'], 2, '.', ','); - $vehList[$v->unique_id]['profit']['currency_info'] = $currencyInfo['currency_symbol'].number_format($vehList[$v->unique_id]['profit']['amount']*$vehList[$v->unique_id]['num'], 2, '.', ','); - continue; - } - - $index += 1; - - // 处理金额 (每次循环重新初始化) - $mv = []; - foreach ($v->moneyValues as $money) { - $mv[$money->field] = [ - 'currency' => $money->currency, - 'amount' => $money->amount, - 'rate' => $money->exchange_rate, - 'currency_info' => SysCurrency::where('currency_code', $money->currency)->value('currency_symbol').number_format($money->amount, 2, '.', ',') - ]; - } - - // 处理改装 (每次循环重新初始化) - $mfy = ''; - foreach ($v->options as $my) { - $mfy .= $my->option_name_snapshot.' '; - } - - // 计算单项利润并累加到总利润 - $itemProfit = $this->calculateProfit($mv, $item); - $totalProfit += $itemProfit; - - // 处理结果集 - $vehListItem = [ - 'index' => $index, - 'model_snapshot' => $v->model_snapshot, - 'color_interior_snapshot' => $v->color_interior_snapshot, - 'color_exterior_snapshot' => $v->color_exterior_snapshot, - 'num' => 1, - 'modify' => $mfy, - 'mileage' => $v->mileage - ]; - - $vehList[$v->unique_id] = $vehListItem + $mv; - } - - $mvs = []; - foreach ($item->moneyValues as $moneys) { - $mvs[$moneys->field] = [ - 'currency' => $moneys->currency, - 'amount' => $moneys->amount, - 'rate' => $moneys->exchange_rate, - 'currency_info' => SysCurrency::where('currency_code', $moneys->currency)->value('currency_symbol').number_format($moneys->amount, 2, '.', ','), - ]; - } - - $accList = []; - foreach ($item->accounts as $k => $v) { - $accList[] = [ - 'index' => '收款'.($k + 1), - 'remit_country_name_snapshot' => $v->remit_country_name_snapshot, - 'remit_date' => date('Y-m-d', $v->remit_date), - 'currency_name_snapshot' => $v->currency_name_snapshot, - 'account_name_snapshot' => $v->account_name_snapshot, - 'remitter_name' => $v->remitter_name, - 'amount' => $v->amount, - ]; - } - - - - $veh = [ - 'order_no' => $item->order_no, - 'salesperson_snapshot' => $item->salesperson_snapshot, - 'sign_img_url' => (!empty($item->salesperson->sign_img)) ? - (new \app\common\service\OssService)->generatePrivateUrl($item->salesperson->sign_img) : '', - 'created_at' => date('Y-m-d', strtotime($item->created_at)), - - 'pi_no' => $item->pi_no, - 'biz_date' => date('Y-m-d', $item->biz_date), - 'clearance_country_snapshot' => $item->clearance_country_snapshot, - 'customer_source_snapshot' => $item->customer_source_snapshot, - 'customer_snapshot' => $item->customer_snapshot, - 'customer_country_snapshot' => $item->customer_country_snapshot, - - // 订单项 - 'veh_list' => array_values($vehList), // 重新索引数组 - - 'contract_exchange_rate' => $item->contract_exchange_rate, - 'cif_total_amount' => $mvs['cif_total_amount'], - 'profit_total' => $mvs['profit_total'], - - 'declarant_name' => $item->declarant_name, - 'eta_ship_date' => date('Y-m-d', $item->eta_ship_date), - 'shipping_method_snapshot' => $item->shipping_method_snapshot, - 'domestic_port_snapshot' => $item->domestic_port_snapshot, - 'trade_term_snapshot' => $item->trade_term_snapshot, - 'clearance_port_snapshot' => $item->clearance_port_snapshot, - 'latest_arrival_date' => date('Y-m-d', $item->latest_arrival_date), - 'pick_up_date' => date('Y-m-d', $item->pick_up_date), - 'collect_all_date' => date('Y-m-d', $item->collect_all_date), - 'is_final' => $item->is_final, - - // 账户 - 'account_list' => $accList, - 'acc_index' => count($accList)+1, - 'currency_symbol' => $item->settlement_currency, - 'sign_imgs' => $this->queryAppro($item->id), - 'contract_url' => (!empty($item->contract)) ? (new \app\common\service\OssService)->generatePrivateUrl($item->contract) : '' - ]; - - return $veh; - } - - - public function queryAppro($bizId) - { - $instances = ApprovalInstances::where('biz_id', $bizId)->where('biz_type', 'stock_orders')->first(); - if (!$instances) { - return []; - } - - $records = ApprovalRecords::where('instance_id', $instances->id)->where('action', 'approve')->orderBy('step_index', 'asc')->pluck('approver_id')->toArray(); - if (count($records) == 0) { - return []; - } - - $signImgs = []; - foreach ($records as $k => $v) { - $signImg = User::where('id', $v)->value('sign_img'); - $signImgs[$k] = (!empty($signImg))?(new \app\common\service\OssService)->generatePrivateUrl($signImg):''; - } - - return $signImgs; - } - - /** - * 打印数据 - */ - public function servicePrint(Request $request) - { - $config = $this->buildQueryConfig($request, 'show'); - $item = $this->logicShow($config); - return $this->postProcess('Print', $item); - } - - public function serviceDetail(Request $request) - { - $config = $this->buildQueryConfig($request, 'detail'); - $item = $this->logicShow($config); - $pdfData = $this->pdfData($item); - return $this->postProcess('Detail', $pdfData); - } - - /** - * 审批 - */ - public function serviceApproval(Request $request) - { - $config = $this->buildQueryConfig($request, 'approval'); - $this->logicUpdateBy($config, [ - 'approval_status' => 1 - ]); - $item = $this->logicShow($config); - $this->startApproval($item); - return $this->postProcess('Approval', $item); - } /** * 最终审批 @@ -412,351 +83,4 @@ class StockOrdersService $this->startApproval($item); return $this->postProcess('UltimateApproval', $item); } - - /** - * 创建 - * - * @param Request $request - * @param array $data - * @return mixed - */ - public function serviceStore(Request $request, array $data = []) - { - // 关联表较多,增加事务处理 - return Db::transaction(function () use ($request, $data) { - $data = $this->preProcess('Store', $data, $request); - $dl = $this->getDataLimitFromRequest($request); - $created = $this->logicCreate($data, $dl); - - // 处理车辆和账户信息 - $this->processOrderItems($created, $data); - - return $this->postProcess('Store', $created); - }); - } - - protected function onlyFillableData(Model|string $model, array $data, array $extraWhitelist = []): array - { - // 兼容传类名或实例 - $m = \is_string($model) ? new $model : $model; - // 1) 优先使用 fillable - $keys = $m->getFillable(); - // 2) 若没配置 fillable(常见:用 guarded),回退到完整表字段,避免误伤 - if (empty($keys)) { - $keys = $m->getConnection() - ->getSchemaBuilder() - ->getColumnListing($m->getTable()); - } - // 3) 允许额外白名单(如虚拟字段、JSON映射字段) - if ($extraWhitelist) { - $keys = array_merge($keys, $extraWhitelist); - } - return array_intersect_key($data, array_flip($keys)); - } - - /** - * 更新前处理 - * - * @param array $data - * @param Request $request - * @return array - */ - public function beforeServiceUpdate($data, Request $request) - { - $data['biz_date'] = strtotime($data['biz_date']); - $data['eta_ship_date'] = strtotime($data['eta_ship_date']); - $data['latest_arrival_date'] = strtotime($data['latest_arrival_date']); - $data['approval_status'] = $data['approval_status'] == 1 ? 1 : 0; - - $data['pick_up_date'] = $data['pick_up_date'] ? strtotime($data['pick_up_date']) : 0; - $data['collect_all_date'] = $data['collect_all_date'] ? strtotime($data['collect_all_date']) : 0; - return $data; - } - - /** - * 更新后处理 - * - * @param mixed $res - * @return mixed - */ - protected function afterServiceUpdate($res) - { - // 启动审批 - if ($res->approval_status == 1) { - $this->startApproval($res); - } - return $res; - } - - /** - * 更新 - * - * @param Request $request - * @param array $data - * @return mixed - */ - public function serviceUpdate(Request $request, array $data = []) - { - // 关联表较多,增加事务处理 - return Db::transaction(function () use ($request, $data) { - $data = $this->preProcess('Update', $data, $request); - $config = $this->buildQueryConfig($request, 'update'); - // 过滤掉非库字段 - $dbData = $this->onlyFillableData($config['model'], $data); - // 更新主表 - $updated = $this->logicUpdateBy($config, $dbData); - // 获取更新后的数据 - $order = $this->logicShow($config); - // 处理车辆和账户信息 - $this->processOrderItems($order, $data, true); - return $this->postProcess('Update', $order); - }); - } - - /** - * 处理订单项(车辆和账户) - * @param StockOrders $order 订单对象 - * @param array $data 数据 - * @param bool $isUpdate 是否为更新操作 - */ - private function processOrderItems($order, array $data, bool $isUpdate = false): void - { - // 如果是更新操作,先删除原有的关联数据 - if ($isUpdate) { - $this->clearExistingOrderItems($order->id); - } - - // 处理车辆项 - $this->processVehicleItems($order, $data['veh_list'] ?? []); - - // 处理账户项 - $this->processAccountItems($order, $data['account_list'] ?? []); - } - - /** - * (更新时)清除已存在的订单项 - * @param int $orderId 订单ID - */ - private function clearExistingOrderItems(int $orderId): void - { - // 删除车辆改装项 - StockOrderItemOptions::whereHas('item', function ($query) use ($orderId) { - $query->where('order_id', $orderId); - })->delete(); - - // 删除车辆项及其相关金额记录 - $orderItems = StockOrderItems::where('order_id', $orderId)->get(); - foreach ($orderItems as $item) { - // 删除车辆项的金额记录 - Db::table('veh_money_value') - ->where('object_scene', 'stock_order_items') - ->where('object_id', $item->id) - ->delete(); - } - - // 删除车辆项 - StockOrderItems::where('order_id', $orderId)->delete(); - - // 删除订单本身的金额记录 - Db::table('veh_money_value') - ->where('object_scene', 'stock_orders') - ->where('object_id', $orderId) - ->delete(); - - // 删除账户项 - StockOrderAccounts::where('order_id', $orderId)->delete(); - } - - /** - * 处理车辆项 - */ - private function processVehicleItems($order, array $vehList): void - { - $cifTotalAmount = 0.00; - // $cifTotalAmountRate = 0; - // $cifTotalAmountCurrency = 'CNY'; - - $profitTotalAmount = 0.00; - foreach ($vehList as $veh) { - $veh['order_id'] = $order->id; - $veh['unique_id'] = $this->generateVehicleUniqueId($veh); - - $item = StockOrderItems::create($veh); - - // 处理金额信息 - $this->processVehiclePrices($item, $veh); - - // 计算利润 - $profit = $this->calculateProfit($veh, $order); - $item->addMoney('profit', $order->settlement_currency, $profit, $order->contract_exchange_rate, '利润'); - - // 累计到岸价 - $cifTotalAmount += $veh['total_price']['amount'] ?? 0; - $profitTotalAmount += $profit; - - // if (isset($veh['retrofit_fee']['rate'])) { - // $cifTotalAmountRate = $veh['retrofit_fee']['rate']; - // } - // if (isset($veh['retrofit_fee']['currency'])) { - // $cifTotalAmountCurrency = $veh['retrofit_fee']['currency']; - // } - - // 处理改装项 - $this->processModificationItems($item->id, $veh['modify_list'] ?? []); - } - - // 保存合计到岸价 - $order->addMoney('cif_total_amount', $order->settlement_currency, $cifTotalAmount, $order->contract_exchange_rate, '合计到岸价'); - // 利润 - $order->addMoney('profit_total', $order->settlement_currency, $profitTotalAmount, $order->contract_exchange_rate, '总利润'); - } - - /** - * 生成车辆唯一标识 - */ - private function generateVehicleUniqueId(array $veh): string - { - $uniqueIdStr = ""; - $uniqueIdStr .= $veh['sku_id'] ?? ''; - $uniqueIdStr .= $veh['model_snapshot'] ?? ''; - $uniqueIdStr .= $veh['color_interior_code'] ?? ''; - $uniqueIdStr .= $veh['color_exterior_code'] ?? ''; - $uniqueIdStr .= $veh['mileage'] ?? ''; - $uniqueIdStr .= json_encode($veh['modify_list'] ?? []); - - return md5($uniqueIdStr); - } - - /** - * 处理车辆价格信息 - */ - private function processVehiclePrices($item, array $veh): void - { - // 基础费用 - $priceFields = [ - 'unit_price' => '单价', - 'total_price' => '总价', - 'handling_fee' => '手续费', - 'fob_transport_fee' => 'FOB运输费', - 'port_misc_fee' => '港杂', - 'retrofit_fee' => '改装费', - 'ocean_freight' => '海运费' - ]; - - foreach ($priceFields as $field => $description) { - if (!empty($veh[$field]['amount'])) { - $item->addMoney( - $field, - $veh[$field]['currency'], - $veh[$field]['amount'], - $veh[$field]['rate'], - $description - ); - } - } - - // 根据里程处理新车或二手车价格 - if (!empty($veh['mileage']) && $veh['mileage'] > 0) { - // 二手车 - if (!empty($veh['listing_price']['amount'])) { - $item->addMoney( - 'listing_price', - $veh['listing_price']['currency'], - $veh['listing_price']['amount'], - $veh['listing_price']['rate'], - '二手挂牌价' - ); - } - - if (!empty($veh['reduction_price']['amount'])) { - $item->addMoney( - 'reduction_price', - $veh['reduction_price']['currency'], - $veh['reduction_price']['amount'], - $veh['reduction_price']['rate'], - '二手减价' - ); - } - } else { - // 新车 - if (!empty($veh['official_price']['amount'])) { - $item->addMoney( - 'official_price', - $veh['official_price']['currency'], - $veh['official_price']['amount'], - $veh['official_price']['rate'], - '官方指导价' - ); - } - - if (!empty($veh['discount_price']['amount'])) { - $item->addMoney( - 'discount_price', - $veh['discount_price']['currency'], - $veh['discount_price']['amount'], - $veh['discount_price']['rate'], - '优惠价' - ); - } - } - } - - /** - * 计算利润 - */ - private function calculateProfit(array $veh, $order): float - { - $unitPrice = (float)($veh['unit_price']['amount'] ?? 0); - - if (!empty($veh['mileage']) && $veh['mileage'] > 0) { - // 二手车 - $ttPrice = ($veh['listing_price']['amount'] ?? 0) / ($order->contract_exchange_rate ?: 1); - $ddPrice = $veh['reduction_price']['amount'] ?? 0; - } else { - // 新车 - $ttPrice = ($veh['official_price']['amount'] ?? 0) / ($order->contract_exchange_rate ?: 1); - $ddPrice = $veh['discount_price']['amount'] ?? 0; - } - - $handlingFee = (float)($veh['handling_fee']['amount'] ?? 0); - $fobTransportFee = (float)($veh['fob_transport_fee']['amount'] ?? 0); - $portMiscFee = (float)($veh['port_misc_fee']['amount'] ?? 0); - $retrofitFee = (float)($veh['retrofit_fee']['amount'] ?? 0); - $oceanFreight = (float)($veh['ocean_freight']['amount'] ?? 0); - - return $unitPrice - $ttPrice - $ddPrice - $handlingFee - $fobTransportFee - $portMiscFee - $retrofitFee - $oceanFreight; - } - - /** - * 处理改装项 - */ - private function processModificationItems(int $orderItemId, array $modifyList): void - { - if (empty($modifyList)) { - return; - } - - foreach ($modifyList as $modify) { - $modify['order_item_id'] = $orderItemId; - StockOrderItemOptions::create($modify); - } - } - - /** - * 处理账户项 - */ - private function processAccountItems($order, array $accountList): void - { - if (empty($accountList)) { - return; - } - - foreach ($accountList as $account) { - $account['order_id'] = $order->id; - if (!empty($account['remit_date'])) { - $account['remit_date'] = strtotime($account['remit_date']); - } - StockOrderAccounts::create($account); - } - } } \ No newline at end of file diff --git a/app/crm/service/StockOrdersService_bak2.php b/app/crm/service/StockOrdersService_bak2.php new file mode 100644 index 0000000..1c4f7ea --- /dev/null +++ b/app/crm/service/StockOrdersService_bak2.php @@ -0,0 +1,762 @@ +validator; + } + + + protected function buildQueryConfig(Request $request, string $scene = 'index'): array + { + $p = $request->all(); + + $config = []; + $config['model'] = $this->modelClass; + switch ($scene) { + case 'index': + + // ====== ====== 筛选 ====== ====== + // 业务id + if (isset($p['salesperson_id']) && $p['salesperson_id']) { + $config['where']['salesperson_id'] = $p['salesperson_id']; + } + // 初、终版 + $config['where']['is_final'] = $p['is_final'] ?? 0; + // 关键字 + if (isset($p['keyword']) && $p['keyword']) { + $k = '%' . $p['keyword'] . '%'; + foreach (['pi_no', 'order_no', 'customer_snapshot'] as $col) { + $config['orWhere'][] = [$col, 'like', $k]; + } + } + + $config['order']['id'] = 'desc'; + + $config['where']['salesperson_id'] = $request->user->uid; + break; + case 'print': + case 'approval': + $config['where']['id'] = $p['id']; + break; + case 'detail': + case 'show': + $config['where']['id'] = $p['id']; + + $config['with'][] = 'items'; + $config['with'][] = 'items.options'; + $config['with'][] = 'items.moneyValues'; + $config['with'][] = 'accounts'; + $config['with'][] = 'moneyValues'; + $config['with'][] = 'salesperson'; + break; + case 'store': + break; + case 'update': + $config['where']['id'] = $p['id']; + break; + case 'destroy': + $config['where']['id'] = $p['id']; + break; + } + + return $config; + } + + public function beforeServiceStore($data, Request $request) + { + $userId = $request->user->uid; + + $data['order_no'] = generateStockNo(); + $data['biz_date'] = strtotime($data['biz_date']); + $data['eta_ship_date'] = strtotime($data['eta_ship_date']); + $data['latest_arrival_date'] = strtotime($data['latest_arrival_date']); + $data['salesperson_id'] = $userId; + $data['salesperson_snapshot'] = User::where('id', $userId)->value('name') ?? ''; + $data['approval_status'] = $data['approval_status'] == 1 ? 1 : 0; + + // if ($data['is_final'] == 1) { + $data['pick_up_date'] = $data['pick_up_date'] ? strtotime($data['pick_up_date']) : 0; + $data['collect_all_date'] = $data['collect_all_date'] ? strtotime($data['collect_all_date']) : 0; + // } + return $data; + } + + protected function afterServiceStore($res) + { + // 处理原单 + if ($res->is_final == 1 && $res->object_id > 0) { + $order = StockOrders::find($res->object_id); + $order->is_use = 1; + $order->object_id = $res->id; + if (!$order->save()) { + flg('备货单【'.$order->order_no.'】创建终版成功,但更新原单失败'); + } + } + // 启动审批 + if ($res->approval_status == 1) { + $this->startApproval($res); + } + return $res; + } + + /** + * 启动审批 + */ + public function startApproval($item) + { + // 启动审批 + $appro = new \app\common\utils\approval\service\ApprovalFlowService(); + $appro->start('stock_orders', 'stock_orders', $item->id, [], $item->salesperson_id); + // 异步生成pdf文件 + \Webman\RedisQueue\Redis::send('stock-order-pdf', ['id'=>$item->id]); + } + + /** + * 创建PDF文件 + */ + public function createdPdfFile($id) + { + $config = []; + $config['model'] = $this->modelClass; + $config['where']['id'] = $id; + $config['with'][] = 'items'; + $config['with'][] = 'items.options'; + $config['with'][] = 'items.moneyValues'; + $config['with'][] = 'accounts'; + $config['with'][] = 'moneyValues'; + $config['with'][] = 'salesperson'; + $item = $this->logicShow($config); + $pdfData = $this->pdfData($item); + + // 渲染模板为最终HTML + $tplPath = app_path('common/utils/pdf/template/stockOrder.stub'); + $html = \app\common\utils\pdf\service\PdfHelper::render($tplPath, ['data' => $pdfData]); + + // 生成PDF + $driver = new \app\common\utils\pdf\service\MpdfDriver(); + $pdfBin = $driver->htmlToPdf($html); + + // 存储 + $dir = public_path('pdf').'/'; + $fileName = ($pdfData['order_no'] ?? date('YmdHis')) . '.pdf'; + + // 本地保存 + \app\common\utils\pdf\service\PdfStorage::saveBinary( + $pdfBin, + $dir, + $fileName + ); + + // 上传云端 + $param = [ + 'path' => $dir . $fileName, + 'scene' => 'stock_orders', + 'category' => 'pdf', + 'original_name' => $fileName + ]; + $up = new \app\common\service\UploadService(); + $res = $up->uploadLocalFile($param); + + @unlink($dir . $fileName); + + // 更新文件地址 + $item->file_url = $res['file_name']; + $item->save(); + + return $res; + } + + public function pdfData($item) + { + $vehList = []; + $index = 0; + $totalProfit = 0; // 总利润 + + // $priceFields = [ + // 'official_price', // '官方指导价', + // 'discount_price', // '优惠', + // 'listing_price', // '二手挂牌价价', + // 'reduction_price', // '优惠', + + // 'handling_fee', // '手续费', + // 'retrofit_fee', // '改装费', + // 'fob_transport_fee', // 'FOB运输费', + // 'port_misc_fee', // '港杂', + // 'ocean_freight', // '海运费', + + // 'unit_price', // '单价', + // 'total_price', // '总价', + // // 'profit', // '利润' + // ]; + + foreach ($item->items as $k => $v) { + $currencyInfo = SysCurrency::where('currency_code', $item->settlement_currency)->first(['currency_symbol', 'currency_name'])->toArray(); + + // 存在则数量加1 + if (count($vehList) > 0 && isset($vehList[$v->unique_id])) { + $vehList[$v->unique_id]['num'] += 1; + $vehList[$v->unique_id]['total_price']['currency_info'] = $currencyInfo['currency_symbol'].number_format($vehList[$v->unique_id]['total_price']['amount']*$vehList[$v->unique_id]['num'], 2, '.', ','); + $vehList[$v->unique_id]['profit']['currency_info'] = $currencyInfo['currency_symbol'].number_format($vehList[$v->unique_id]['profit']['amount']*$vehList[$v->unique_id]['num'], 2, '.', ','); + continue; + } + + $index += 1; + + // 处理金额 (每次循环重新初始化) + $mv = []; + foreach ($v->moneyValues as $money) { + $mv[$money->field] = [ + 'currency' => $money->currency, + 'amount' => $money->amount, + 'rate' => $money->exchange_rate, + 'currency_info' => SysCurrency::where('currency_code', $money->currency)->value('currency_symbol').number_format($money->amount, 2, '.', ',') + ]; + } + + // 处理改装 (每次循环重新初始化) + $mfy = ''; + foreach ($v->options as $my) { + $mfy .= $my->option_name_snapshot.' '; + } + + // 计算单项利润并累加到总利润 + $itemProfit = $this->calculateProfit($mv, $item); + $totalProfit += $itemProfit; + + // 处理结果集 + $vehListItem = [ + 'index' => $index, + 'model_snapshot' => $v->model_snapshot, + 'color_interior_snapshot' => $v->color_interior_snapshot, + 'color_exterior_snapshot' => $v->color_exterior_snapshot, + 'num' => 1, + 'modify' => $mfy, + 'mileage' => $v->mileage + ]; + + $vehList[$v->unique_id] = $vehListItem + $mv; + } + + $mvs = []; + foreach ($item->moneyValues as $moneys) { + $mvs[$moneys->field] = [ + 'currency' => $moneys->currency, + 'amount' => $moneys->amount, + 'rate' => $moneys->exchange_rate, + 'currency_info' => SysCurrency::where('currency_code', $moneys->currency)->value('currency_symbol').number_format($moneys->amount, 2, '.', ','), + ]; + } + + $accList = []; + foreach ($item->accounts as $k => $v) { + $accList[] = [ + 'index' => '收款'.($k + 1), + 'remit_country_name_snapshot' => $v->remit_country_name_snapshot, + 'remit_date' => date('Y-m-d', $v->remit_date), + 'currency_name_snapshot' => $v->currency_name_snapshot, + 'account_name_snapshot' => $v->account_name_snapshot, + 'remitter_name' => $v->remitter_name, + 'amount' => $v->amount, + ]; + } + + + + $veh = [ + 'order_no' => $item->order_no, + 'salesperson_snapshot' => $item->salesperson_snapshot, + 'sign_img_url' => (!empty($item->salesperson->sign_img)) ? + (new \app\common\service\OssService)->generatePrivateUrl($item->salesperson->sign_img) : '', + 'created_at' => date('Y-m-d', strtotime($item->created_at)), + + 'pi_no' => $item->pi_no, + 'biz_date' => date('Y-m-d', $item->biz_date), + 'clearance_country_snapshot' => $item->clearance_country_snapshot, + 'customer_source_snapshot' => $item->customer_source_snapshot, + 'customer_snapshot' => $item->customer_snapshot, + 'customer_country_snapshot' => $item->customer_country_snapshot, + + // 订单项 + 'veh_list' => array_values($vehList), // 重新索引数组 + + 'contract_exchange_rate' => $item->contract_exchange_rate, + 'cif_total_amount' => $mvs['cif_total_amount'], + 'profit_total' => $mvs['profit_total'], + + 'declarant_name' => $item->declarant_name, + 'eta_ship_date' => date('Y-m-d', $item->eta_ship_date), + 'shipping_method_snapshot' => $item->shipping_method_snapshot, + 'domestic_port_snapshot' => $item->domestic_port_snapshot, + 'trade_term_snapshot' => $item->trade_term_snapshot, + 'clearance_port_snapshot' => $item->clearance_port_snapshot, + 'latest_arrival_date' => date('Y-m-d', $item->latest_arrival_date), + 'pick_up_date' => date('Y-m-d', $item->pick_up_date), + 'collect_all_date' => date('Y-m-d', $item->collect_all_date), + 'is_final' => $item->is_final, + + // 账户 + 'account_list' => $accList, + 'acc_index' => count($accList)+1, + 'currency_symbol' => $item->settlement_currency, + 'sign_imgs' => $this->queryAppro($item->id), + 'contract_url' => (!empty($item->contract)) ? (new \app\common\service\OssService)->generatePrivateUrl($item->contract) : '' + ]; + + return $veh; + } + + + public function queryAppro($bizId) + { + $instances = ApprovalInstances::where('biz_id', $bizId)->where('biz_type', 'stock_orders')->first(); + if (!$instances) { + return []; + } + + $records = ApprovalRecords::where('instance_id', $instances->id)->where('action', 'approve')->orderBy('step_index', 'asc')->pluck('approver_id')->toArray(); + if (count($records) == 0) { + return []; + } + + $signImgs = []; + foreach ($records as $k => $v) { + $signImg = User::where('id', $v)->value('sign_img'); + $signImgs[$k] = (!empty($signImg))?(new \app\common\service\OssService)->generatePrivateUrl($signImg):''; + } + + return $signImgs; + } + + /** + * 打印数据 + */ + public function servicePrint(Request $request) + { + $config = $this->buildQueryConfig($request, 'show'); + $item = $this->logicShow($config); + return $this->postProcess('Print', $item); + } + + public function serviceDetail(Request $request) + { + $config = $this->buildQueryConfig($request, 'detail'); + $item = $this->logicShow($config); + $pdfData = $this->pdfData($item); + return $this->postProcess('Detail', $pdfData); + } + + /** + * 审批 + */ + public function serviceApproval(Request $request) + { + $config = $this->buildQueryConfig($request, 'approval'); + $this->logicUpdateBy($config, [ + 'approval_status' => 1 + ]); + $item = $this->logicShow($config); + $this->startApproval($item); + return $this->postProcess('Approval', $item); + } + + /** + * 最终审批 + */ + public function serviceUltimateApproval(Request $request) + { + $config = $this->buildQueryConfig($request, 'detail'); + $item = $this->logicShow($config); + foreach ($item->items as $k => $v) { + if ($v->vin === '') { + throw new BusinessException('存在未填写vin的车辆,无法完成提交审核'); + } + } + $this->logicUpdateBy($config, [ + 'approval_status' => 1 + ]); + $this->startApproval($item); + return $this->postProcess('UltimateApproval', $item); + } + + /** + * 创建 + * + * @param Request $request + * @param array $data + * @return mixed + */ + public function serviceStore(Request $request, array $data = []) + { + // 关联表较多,增加事务处理 + return Db::transaction(function () use ($request, $data) { + $data = $this->preProcess('Store', $data, $request); + $dl = $this->getDataLimitFromRequest($request); + $created = $this->logicCreate($data, $dl); + + // 处理车辆和账户信息 + $this->processOrderItems($created, $data); + + return $this->postProcess('Store', $created); + }); + } + + protected function onlyFillableData(Model|string $model, array $data, array $extraWhitelist = []): array + { + // 兼容传类名或实例 + $m = \is_string($model) ? new $model : $model; + // 1) 优先使用 fillable + $keys = $m->getFillable(); + // 2) 若没配置 fillable(常见:用 guarded),回退到完整表字段,避免误伤 + if (empty($keys)) { + $keys = $m->getConnection() + ->getSchemaBuilder() + ->getColumnListing($m->getTable()); + } + // 3) 允许额外白名单(如虚拟字段、JSON映射字段) + if ($extraWhitelist) { + $keys = array_merge($keys, $extraWhitelist); + } + return array_intersect_key($data, array_flip($keys)); + } + + /** + * 更新前处理 + * + * @param array $data + * @param Request $request + * @return array + */ + public function beforeServiceUpdate($data, Request $request) + { + $data['biz_date'] = strtotime($data['biz_date']); + $data['eta_ship_date'] = strtotime($data['eta_ship_date']); + $data['latest_arrival_date'] = strtotime($data['latest_arrival_date']); + $data['approval_status'] = $data['approval_status'] == 1 ? 1 : 0; + + $data['pick_up_date'] = $data['pick_up_date'] ? strtotime($data['pick_up_date']) : 0; + $data['collect_all_date'] = $data['collect_all_date'] ? strtotime($data['collect_all_date']) : 0; + return $data; + } + + /** + * 更新后处理 + * + * @param mixed $res + * @return mixed + */ + protected function afterServiceUpdate($res) + { + // 启动审批 + if ($res->approval_status == 1) { + $this->startApproval($res); + } + return $res; + } + + /** + * 更新 + * + * @param Request $request + * @param array $data + * @return mixed + */ + public function serviceUpdate(Request $request, array $data = []) + { + // 关联表较多,增加事务处理 + return Db::transaction(function () use ($request, $data) { + $data = $this->preProcess('Update', $data, $request); + $config = $this->buildQueryConfig($request, 'update'); + // 过滤掉非库字段 + $dbData = $this->onlyFillableData($config['model'], $data); + // 更新主表 + $updated = $this->logicUpdateBy($config, $dbData); + // 获取更新后的数据 + $order = $this->logicShow($config); + // 处理车辆和账户信息 + $this->processOrderItems($order, $data, true); + return $this->postProcess('Update', $order); + }); + } + + /** + * 处理订单项(车辆和账户) + * @param StockOrders $order 订单对象 + * @param array $data 数据 + * @param bool $isUpdate 是否为更新操作 + */ + private function processOrderItems($order, array $data, bool $isUpdate = false): void + { + // 如果是更新操作,先删除原有的关联数据 + if ($isUpdate) { + $this->clearExistingOrderItems($order->id); + } + + // 处理车辆项 + $this->processVehicleItems($order, $data['veh_list'] ?? []); + + // 处理账户项 + $this->processAccountItems($order, $data['account_list'] ?? []); + } + + /** + * (更新时)清除已存在的订单项 + * @param int $orderId 订单ID + */ + private function clearExistingOrderItems(int $orderId): void + { + // 删除车辆改装项 + StockOrderItemOptions::whereHas('item', function ($query) use ($orderId) { + $query->where('order_id', $orderId); + })->delete(); + + // 删除车辆项及其相关金额记录 + $orderItems = StockOrderItems::where('order_id', $orderId)->get(); + foreach ($orderItems as $item) { + // 删除车辆项的金额记录 + Db::table('veh_money_value') + ->where('object_scene', 'stock_order_items') + ->where('object_id', $item->id) + ->delete(); + } + + // 删除车辆项 + StockOrderItems::where('order_id', $orderId)->delete(); + + // 删除订单本身的金额记录 + Db::table('veh_money_value') + ->where('object_scene', 'stock_orders') + ->where('object_id', $orderId) + ->delete(); + + // 删除账户项 + StockOrderAccounts::where('order_id', $orderId)->delete(); + } + + /** + * 处理车辆项 + */ + private function processVehicleItems($order, array $vehList): void + { + $cifTotalAmount = 0.00; + // $cifTotalAmountRate = 0; + // $cifTotalAmountCurrency = 'CNY'; + + $profitTotalAmount = 0.00; + foreach ($vehList as $veh) { + $veh['order_id'] = $order->id; + $veh['unique_id'] = $this->generateVehicleUniqueId($veh); + + $item = StockOrderItems::create($veh); + + // 处理金额信息 + $this->processVehiclePrices($item, $veh); + + // 计算利润 + $profit = $this->calculateProfit($veh, $order); + $item->addMoney('profit', $order->settlement_currency, $profit, $order->contract_exchange_rate, '利润'); + + // 累计到岸价 + $cifTotalAmount += $veh['total_price']['amount'] ?? 0; + $profitTotalAmount += $profit; + + // if (isset($veh['retrofit_fee']['rate'])) { + // $cifTotalAmountRate = $veh['retrofit_fee']['rate']; + // } + // if (isset($veh['retrofit_fee']['currency'])) { + // $cifTotalAmountCurrency = $veh['retrofit_fee']['currency']; + // } + + // 处理改装项 + $this->processModificationItems($item->id, $veh['modify_list'] ?? []); + } + + // 保存合计到岸价 + $order->addMoney('cif_total_amount', $order->settlement_currency, $cifTotalAmount, $order->contract_exchange_rate, '合计到岸价'); + // 利润 + $order->addMoney('profit_total', $order->settlement_currency, $profitTotalAmount, $order->contract_exchange_rate, '总利润'); + } + + /** + * 生成车辆唯一标识 + */ + private function generateVehicleUniqueId(array $veh): string + { + $uniqueIdStr = ""; + $uniqueIdStr .= $veh['sku_id'] ?? ''; + $uniqueIdStr .= $veh['model_snapshot'] ?? ''; + $uniqueIdStr .= $veh['color_interior_code'] ?? ''; + $uniqueIdStr .= $veh['color_exterior_code'] ?? ''; + $uniqueIdStr .= $veh['mileage'] ?? ''; + $uniqueIdStr .= json_encode($veh['modify_list'] ?? []); + + return md5($uniqueIdStr); + } + + /** + * 处理车辆价格信息 + */ + private function processVehiclePrices($item, array $veh): void + { + // 基础费用 + $priceFields = [ + 'unit_price' => '单价', + 'total_price' => '总价', + 'handling_fee' => '手续费', + 'fob_transport_fee' => 'FOB运输费', + 'port_misc_fee' => '港杂', + 'retrofit_fee' => '改装费', + 'ocean_freight' => '海运费' + ]; + + foreach ($priceFields as $field => $description) { + if (!empty($veh[$field]['amount'])) { + $item->addMoney( + $field, + $veh[$field]['currency'], + $veh[$field]['amount'], + $veh[$field]['rate'], + $description + ); + } + } + + // 根据里程处理新车或二手车价格 + if (!empty($veh['mileage']) && $veh['mileage'] > 0) { + // 二手车 + if (!empty($veh['listing_price']['amount'])) { + $item->addMoney( + 'listing_price', + $veh['listing_price']['currency'], + $veh['listing_price']['amount'], + $veh['listing_price']['rate'], + '二手挂牌价' + ); + } + + if (!empty($veh['reduction_price']['amount'])) { + $item->addMoney( + 'reduction_price', + $veh['reduction_price']['currency'], + $veh['reduction_price']['amount'], + $veh['reduction_price']['rate'], + '二手减价' + ); + } + } else { + // 新车 + if (!empty($veh['official_price']['amount'])) { + $item->addMoney( + 'official_price', + $veh['official_price']['currency'], + $veh['official_price']['amount'], + $veh['official_price']['rate'], + '官方指导价' + ); + } + + if (!empty($veh['discount_price']['amount'])) { + $item->addMoney( + 'discount_price', + $veh['discount_price']['currency'], + $veh['discount_price']['amount'], + $veh['discount_price']['rate'], + '优惠价' + ); + } + } + } + + /** + * 计算利润 + */ + private function calculateProfit(array $veh, $order): float + { + $unitPrice = (float)($veh['unit_price']['amount'] ?? 0); + + if (!empty($veh['mileage']) && $veh['mileage'] > 0) { + // 二手车 + $ttPrice = ($veh['listing_price']['amount'] ?? 0) / ($order->contract_exchange_rate ?: 1); + $ddPrice = $veh['reduction_price']['amount'] ?? 0; + } else { + // 新车 + $ttPrice = ($veh['official_price']['amount'] ?? 0) / ($order->contract_exchange_rate ?: 1); + $ddPrice = $veh['discount_price']['amount'] ?? 0; + } + + $handlingFee = (float)($veh['handling_fee']['amount'] ?? 0); + $fobTransportFee = (float)($veh['fob_transport_fee']['amount'] ?? 0); + $portMiscFee = (float)($veh['port_misc_fee']['amount'] ?? 0); + $retrofitFee = (float)($veh['retrofit_fee']['amount'] ?? 0); + $oceanFreight = (float)($veh['ocean_freight']['amount'] ?? 0); + + return $unitPrice - $ttPrice - $ddPrice - $handlingFee - $fobTransportFee - $portMiscFee - $retrofitFee - $oceanFreight; + } + + /** + * 处理改装项 + */ + private function processModificationItems(int $orderItemId, array $modifyList): void + { + if (empty($modifyList)) { + return; + } + + foreach ($modifyList as $modify) { + $modify['order_item_id'] = $orderItemId; + StockOrderItemOptions::create($modify); + } + } + + /** + * 处理账户项 + */ + private function processAccountItems($order, array $accountList): void + { + if (empty($accountList)) { + return; + } + + foreach ($accountList as $account) { + $account['order_id'] = $order->id; + if (!empty($account['remit_date'])) { + $account['remit_date'] = strtotime($account['remit_date']); + } + StockOrderAccounts::create($account); + } + } +} \ No newline at end of file