diff --git a/app/admin/controller/ApprovalFlowsController.php b/app/admin/controller/ApprovalFlowsController.php new file mode 100644 index 0000000..8f8ae24 --- /dev/null +++ b/app/admin/controller/ApprovalFlowsController.php @@ -0,0 +1,44 @@ +service = new ApprovalFlowsService(); + } + + /** + * 列表页数据格式化 + * + * @param array $data + * @return array + */ + protected function indexFormat($data) + { + if (isset($data['created_at'])) + $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) + $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + return $data; + } + + + public function desc() + { + + + // type sequential(顺序会签), parallel(并行会签), any(或签) + + + + } +} \ No newline at end of file diff --git a/app/admin/logic/ApprovalFlowsLogic.php b/app/admin/logic/ApprovalFlowsLogic.php new file mode 100644 index 0000000..1608782 --- /dev/null +++ b/app/admin/logic/ApprovalFlowsLogic.php @@ -0,0 +1,17 @@ + 'array', + ]; +} \ No newline at end of file diff --git a/app/admin/service/ApprovalFlowsService.php b/app/admin/service/ApprovalFlowsService.php new file mode 100644 index 0000000..3f7a4e9 --- /dev/null +++ b/app/admin/service/ApprovalFlowsService.php @@ -0,0 +1,62 @@ +validator; + } + + + protected function buildQueryConfig(Request $request, string $scene = 'index'): array + { + $p = $request->all(); + + $config = []; + $config['model'] = $this->modelClass; + switch ($scene) { + case 'index': + $config['order']['id'] = 'desc'; + break; + case 'show': + $config['where']['id'] = $p['id']; + break; + case 'store': + break; + case 'update': + $config['where']['id'] = $p['id']; + break; + case 'destroy': + $config['where']['id'] = $p['id']; + break; + } + + return $config; + } + + + +} \ No newline at end of file diff --git a/app/admin/validate/ApprovalFlowsValidate.php b/app/admin/validate/ApprovalFlowsValidate.php new file mode 100644 index 0000000..9b64000 --- /dev/null +++ b/app/admin/validate/ApprovalFlowsValidate.php @@ -0,0 +1,29 @@ + [ + + ], + + 'update' => [ + + ] + ]; +} \ No newline at end of file diff --git a/app/common/model/StockOrderItems.php b/app/common/model/StockOrderItems.php index 610f694..da57006 100644 --- a/app/common/model/StockOrderItems.php +++ b/app/common/model/StockOrderItems.php @@ -43,21 +43,8 @@ class StockOrderItems extends Model */ protected $fillable = [ 'id', 'order_id', 'product_code', 'sku_id', 'sku_snapshot', - 'model_snapshot', 'quantity', 'color_interior_coide', 'color_interior_snapshot', - 'color_exterior_coide', 'color_exterior_snapshot', 'remark', - // 'unit_price', - // 'handling_fee', - // 'fob_transport_fee', - // 'port_misc_fee', - // 'retrofit_fee', - // 'total_price', - 'created_at', 'updated_at', - // 'unit_price_value_id', - // 'handling_fee_value_id', - // 'fob_transport_fee_value_id', - // 'port_misc_fee_value_id', - // 'retrofit_fee_value_id', - // 'total_price_value_id' + 'model_snapshot', 'quantity', 'color_interior_code', 'color_interior_snapshot', + 'color_exterior_code', 'color_exterior_snapshot', 'remark', 'vin', 'created_at', 'updated_at' ]; /** diff --git a/app/common/model/StockOrders.php b/app/common/model/StockOrders.php index 123eb66..2c4c8d3 100644 --- a/app/common/model/StockOrders.php +++ b/app/common/model/StockOrders.php @@ -3,6 +3,7 @@ namespace app\common\model; use support\Model; +use app\common\traits\HasMoneyJournal; class StockOrders extends Model { @@ -58,6 +59,13 @@ class StockOrders extends Model protected $guarded = []; + use HasMoneyJournal; + // 实现抽象方法 + protected static function moneyObjectScene(): string + { + return 'stock_orders'; + } + /** * 关联订单项 */ diff --git a/app/common/service/StockOrdersService.php b/app/common/service/StockOrdersService.php index c694177..17e9fbd 100644 --- a/app/common/service/StockOrdersService.php +++ b/app/common/service/StockOrdersService.php @@ -8,6 +8,7 @@ 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\exception\BusinessException; @@ -45,9 +46,26 @@ class StockOrdersService $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'; break; case 'show': + case 'print': $config['where']['id'] = $p['id']; $config['with'][] = 'items'; @@ -79,6 +97,10 @@ class StockOrdersService $created = $this->logicCreate($data, $dl); // 表1 // 车辆 + $cifTotalAmount = 0; + $cifTotalAmountRate = 0; + $cifTotalAmountCurrency = 'CNY'; + // $profit = 0; foreach ($data['veh_list'] as $veh) { $veh['order_id'] = $created->id; $item = StockOrderItems::create($veh); // 表2 @@ -93,6 +115,15 @@ class StockOrdersService $item->addMoney('port_misc_fee', $veh['port_misc_fee']['currency'], $veh['port_misc_fee']['amount'], $veh['port_misc_fee']['rate'], '港杂'); $item->addMoney('retrofit_fee', $veh['retrofit_fee']['currency'], $veh['retrofit_fee']['amount'], $veh['retrofit_fee']['rate'], '改装费'); + // 利润 + $profit = $veh['unit_price']['amount'] - ($veh['official_price']['amount'] / $created->contract_exchange_rate) - $veh['discount_price']['amount'] - $veh['handling_fee']['amount'] - $veh['fob_transport_fee']['amount'] - $veh['port_misc_fee']['amount'] - $veh['retrofit_fee']['amount']; + $item->addMoney('profit', $veh['unit_price']['currency'], $profit, $veh['unit_price']['rate'], '利润'); + + // cif_total_amount + $cifTotalAmount += $veh['total_price']['amount']; + $cifTotalAmountRate = $veh['retrofit_fee']['rate']; + $cifTotalAmountCurrency = $veh['retrofit_fee']['currency']; + // 改装 if (count($veh['modify_list']) > 0) { foreach ($veh['modify_list'] as $modify) { @@ -102,6 +133,9 @@ class StockOrdersService } } + // 计算&&插入 表1 + $created->addMoney('cif_total_amount', $cifTotalAmountCurrency, $cifTotalAmount, $cifTotalAmountRate, '合计到岸价'); + // 账户 if (count($data['account_list']) > 0) { foreach ($data['account_list'] as $account) { @@ -129,17 +163,156 @@ class StockOrdersService return $data; } - // protected function afterServiceStore($res) - // { - - // return $res; - // } - - - public function serviceShow(Request $request) + protected function afterServiceStore($res) { - $config = $this->buildQueryConfig($request, 'show'); + // 处理原单 + 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.'】创建终版成功,但更新原单失败'); + } + } + return $res; + } + + + public function servicePrint(Request $request) + { + $config = $this->buildQueryConfig($request, 'print'); $item = $this->logicShow($config); - return $this->postProcess('Show', $item); + $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'; + + // 本地保存 + $filePath = \app\common\utils\pdf\service\PdfStorage::saveBinary( + $pdfBin, + $dir, + $fileName + ); + + // // 在线预览 + // return new \support\Response(200, [ + // 'Content-Type' => 'application/pdf', + // 'Content-Disposition' => 'inline; filename="stock-order.pdf"', + // ], $pdfBin); + + return ['file_url' => $dir.$fileName]; + } + + + public function pdfData($item) + { + $vehList = []; + $index = 0; + foreach ($item->items as $k => $v) { + // 存在则数量加1 + if (count($vehList) > 0 && isset($vehList[$v->sku_id])) { + $vehList[$v->sku_id]['num'] += 1; + $vehList[$v->sku_id]['total_price'] += $vehList[$v->sku_id]['unit_price']; + $vehList[$v->sku_id]['total_price_text'] = $vehList[$v->sku_id]['total_price_symbol'].number_format($vehList[$v->sku_id]['total_price'], 2, '.', ','); + continue; + } + $index += 1; + // 处理金额 + $mv = []; + foreach ($v->moneyValues as $money) { + $mv[$money->field] = [ + 'currency' => $money->currency, + 'amount' => $money->amount, + 'rate' => $money->rate, + 'currency_symbol' => SysCurrency::where('currency_code', $money->currency)->value('currency_symbol'), + ]; + } + // 处理改装 + $mfy = ''; + foreach ($v->options as $my) { + $mfy .= $my->option_name_snapshot.' '; + } + + // 处理结果集 + $vehList[$v->sku_id] = [ + '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, + + 'unit_price' => $mv['unit_price']['amount'], + 'total_price' => $mv['total_price']['amount'], + 'official_price' => $mv['official_price']['amount'], + 'discount_price' => $mv['discount_price']['amount'], + + 'unit_price_symbol' => $mv['unit_price']['currency_symbol'], + 'total_price_symbol' => $mv['total_price']['currency_symbol'], + 'official_price_symbol' => $mv['official_price']['currency_symbol'], + 'discount_price_symbol' => $mv['discount_price']['currency_symbol'], + + 'unit_price_text' => $mv['unit_price']['currency_symbol'].number_format($mv['unit_price']['amount'], 2, '.', ','), + 'total_price_text' => $mv['total_price']['currency_symbol'].number_format($mv['total_price']['amount'], 2, '.', ','), + 'official_price_text' => $mv['official_price']['currency_symbol'].number_format($mv['official_price']['amount'], 2, '.', ','), + 'discount_price_text' => $mv['discount_price']['currency_symbol'].number_format($mv['discount_price']['amount'], 2, '.', ','), + ]; + } + + $accList = []; + $accIndex = 1; + 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, + ]; + $accIndex += 1; + } + + $veh = [ + 'order_no' => $item->order_no, + 'salesperson_snapshot' => $item->salesperson_snapshot, + + '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' => $vehList, + + 'contract_exchange_rate' => $item->contract_exchange_rate, + 'cif_total_amount' => $item->cif_total_amount, + + '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), + + // 账户 + 'account_list' => $accList, + 'acc_index' => $accIndex + ]; + + return $veh; } } \ No newline at end of file diff --git a/app/common/approval/model/ApprovalCcs.php b/app/common/utils/approval/model/ApprovalCcs.php similarity index 95% rename from app/common/approval/model/ApprovalCcs.php rename to app/common/utils/approval/model/ApprovalCcs.php index 89be176..f44dfff 100644 --- a/app/common/approval/model/ApprovalCcs.php +++ b/app/common/utils/approval/model/ApprovalCcs.php @@ -1,6 +1,6 @@ '备货单', + 'order_no' => 'SO' . date('YmdHis'), + 'customer' => 'Magic Bridge Auto FZE', + ]); + + $driver = new MpdfDriver(); + $pdf = $driver->htmlToPdf($html, [ + // 若 HTML 里用到相对资源(img/css),给个基准目录 + 'asset_base' => public_path('assets/pdf'), + // 也可以单独传 CSS 文件(推荐把 CSS 放在 public/assets/pdf) + 'css_files' => [ + public_path('assets/pdf/stock-order.css'), + ], + 'header_html' => '
Company • Stock Order
', + 'footer_html' => '
{PAGENO} / {nbpg}
', + 'watermark_text' => '', // 'DRAFT' + ]); + + return new Response(200, [ + 'Content-Type' => 'application/pdf', + 'Content-Disposition' => 'inline; filename="stock-order.pdf"', + ], $pdf); + } + + // ② 直接下载 + public function download(Request $request): Response + { + $html = '

发票

客户:永生花

'; + $pdf = (new MpdfDriver())->htmlToPdf($html); + return new Response(200, [ + 'Content-Type' => 'application/pdf', + 'Content-Disposition' => 'attachment; filename="invoice.pdf"', + ], $pdf); + } + + // ③ 保存到本地(或上传 OSS 前先落盘) + public function save(Request $request): Response + { + $tpl = base_path('resource/templates/invoice.html'); + $html = PdfHelper::loadTemplate($tpl, [ + 'invoice_no' => 'INV' . date('YmdHis'), + 'amount' => '12345.67', + ]); + + $pdf = (new MpdfDriver())->htmlToPdf($html, [ + 'css_files' => [ public_path('assets/pdf/invoice.css') ], + 'asset_base'=> public_path('assets/pdf'), + ]); + + $dir = runtime_path('pdf'); + if (!is_dir($dir)) mkdir($dir, 0777, true); + $path = $dir . '/invoice-' . date('YmdHis') . '.pdf'; + file_put_contents($path, $pdf); + + return json(['path' => $path]); + } +} diff --git a/app/common/utils/pdf/service/MpdfDriver.php b/app/common/utils/pdf/service/MpdfDriver.php new file mode 100644 index 0000000..d59427a --- /dev/null +++ b/app/common/utils/pdf/service/MpdfDriver.php @@ -0,0 +1,118 @@ + 'A4', + 'orientation' => 'L', + 'margin_top' => 12, + 'margin_bottom' => 12, + 'margin_left' => 10, + 'margin_right' => 10, + 'tempDir' => runtime_path('mpdf'), + 'header_html' => '', + 'footer_html' => '', + 'watermark_text'=> '', + 'css_files' => [], + 'default_font' => 'notosanssc', + 'font_dirs' => [public_path('fonts')], + 'font_data' => [ + 'notosanssc' => ['R'=>'NotoSansSC-Regular.ttf','B'=>'NotoSansSC-Bold.ttf'], + ], + 'asset_base' => '', + 'enable_html5_parser' => true, + ], $options); + + $defaultConfig = (new ConfigVariables())->getDefaults(); + $defaultFontConfig = (new FontVariables())->getDefaults(); + + $fontDirs = array_merge($defaultConfig['fontDir'], $opts['font_dirs']); + $fontData = array_merge($defaultFontConfig['fontdata'], $opts['font_data']); + + $mpdf = new Mpdf([ + 'format' => $opts['format'], + 'orientation' => $opts['orientation'], + 'margin_left' => $opts['margin_left'], + 'margin_right' => $opts['margin_right'], + 'margin_top' => $opts['margin_top'], + 'margin_bottom' => $opts['margin_bottom'], + 'tempDir' => $opts['tempDir'], + 'fontDir' => $fontDirs, + 'fontdata' => $fontData, + 'default_font' => $opts['default_font'], + 'enable_html5_parser' => $opts['enable_html5_parser'], + ]); + + if ($opts['header_html']) $mpdf->SetHTMLHeader($opts['header_html']); + if ($opts['footer_html']) $mpdf->SetHTMLFooter($opts['footer_html']); + + if (!empty($opts['watermark_text'])) { + $mpdf->SetWatermarkText($opts['watermark_text']); + $mpdf->showWatermarkText = true; + $mpdf->watermarkTextAlpha = 0.08; + } + + // 资源绝对化(如果你有相对路径的图片等) + $html = PdfHelper::absolutizeAssetUrls($html, $opts['asset_base']); + + // === 关键改动:把 抽出来,作为 HEADER_CSS 写入 === + [$inlineCss, $cleanHtml] = $this->extractCssAndCleanHtml($html); + + // 先写外部 css_files + if (!empty($opts['css_files'])) { + foreach ($opts['css_files'] as $cssFile) { + if (is_file($cssFile)) { + $mpdf->WriteHTML(file_get_contents($cssFile), HTMLParserMode::HEADER_CSS); + } + } + } + // 再写内联 的内容拼成一段 CSS,并把这些 ,DOTALL 支持跨行 + if (preg_match_all('/]*>(.*?)<\/style>/is', $html, $m)) { + foreach ($m[1] as $block) { + // 去掉 HTML 注释,以防注释把 CSS 包起来 + $block = preg_replace('//', '', $block); + $cssParts[] = trim($block); + } + // 从 HTML 删除所有 + + +
+ +
+

合塑实业-汽车事业部-备货单(客户下定)

+
+ 备货单号: + 业务员: +
+
+ + + + + + + + + + + + + + + + + + + + + +
阿里订单号/PI号: 日期: 关目的国: 客户来源: 客户名称: 客户所在国:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
编号车型数量/台颜色/外内饰改装单价(美金)总价(美金)官方指导价优惠
+ 合同汇率: + + 合计到岸价: +
+ + + + + + + + + + + + + + + + + + + + + +
清关人: 预计发运日期: 出运方式: 国内收货港: 贸易条款/清关目的港: /最迟到港时间:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
计划收款信息序号汇款国日期币种账户汇款人金额
+
+ + \ No newline at end of file diff --git a/app/controller/IndexController.php b/app/controller/IndexController.php index 1c69073..8f6428f 100644 --- a/app/controller/IndexController.php +++ b/app/controller/IndexController.php @@ -77,27 +77,27 @@ class IndexController public function createResource() { $options = [ - // 'controller' => [ - // 'module' => 'common', - // 'name' => 'stock_orders' - // ], - 'model' => [ - 'module' => 'approval', - 'name' => 'approval_records' + 'controller' => [ + 'module' => 'admin', + 'name' => 'approval_flows' ], - // 'service' => [ - // 'module' => 'common', - // 'name' => 'stock_orders' - // ], - // 'logic' => [ - // 'module' => 'common', - // 'name' => 'stock_orders' - // ], - // 'validate' => [ - // 'module' => 'common', - // 'name' => 'stock_orders' - // ], - 'table' => 'approval_records' + 'model' => [ + 'module' => 'admin', + 'name' => 'approval_flows' + ], + 'service' => [ + 'module' => 'admin', + 'name' => 'approval_flows' + ], + 'logic' => [ + 'module' => 'admin', + 'name' => 'approval_flows' + ], + 'validate' => [ + 'module' => 'admin', + 'name' => 'approval_flows' + ], + 'table' => 'approval_flows' ]; $result = (new Generate($options))->doGenerate(); @@ -107,46 +107,13 @@ class IndexController public function test() { - $data = [ - 'id' => 1, - 'user_id' => 1, - 'leave_type_id' => 1, - 'start_date' => '2019-01-01', - 'end_date' => '2019-01-02', - 'reason' => 'test', - 'status' => 1 - ]; - $code = $this->submitLeaveApplication(1, $data); - - return $code; - } - - - // 假设这是你的业务逻辑 - public function submitLeaveApplication(int $bizId, $leaveData) { - // 1. 保存业务数据 - // $bizId = DB::table('leave_applications')->insertGetId($leaveData); - - // 2. 启动审批流程 - $service = new \app\common\approval\service\ApprovalFlowService(); - try { - $result = $service->start( - 'leave', // 流程类型,对应 approval_flows.type - 'leave_application', // 业务类型标识 - $bizId, // 业务ID - $leaveData, // 表单数据快照 - $_SESSION['user_id'] ?? 1 // 发起人ID - ); - - // 3. 将审批实例ID关联回业务表 - DB::table('leave_applications') - ->where('id', $bizId) - ->update(['approval_instance_id' => $result['instance_id']]); - - return ['status' => 'success', 'message' => '申请已提交,等待审批']; - } catch (\Exception $e) { - // 处理错误,可能需要回滚业务数据插入 - return ['status' => 'error', 'message' => $e->getMessage()]; - } + return json([ + 'code' => 0, + 'msg' => 'success', + 'data' => [ + 'time' => time(), + 'date' => date('Y-m-d H:i:s') + ] + ]); } } \ No newline at end of file diff --git a/app/crm/controller/StockOrdersController.php b/app/crm/controller/StockOrdersController.php index adc9d92..f32f03f 100644 --- a/app/crm/controller/StockOrdersController.php +++ b/app/crm/controller/StockOrdersController.php @@ -8,6 +8,10 @@ use support\Request; class StockOrdersController extends BaseController { + protected $noNeedLogin = ['print']; + + protected $noNeedHeaderData = ['print']; + use CrudControllerTrait; protected $service; @@ -23,20 +27,104 @@ class StockOrdersController extends BaseController * @param array $data * @return array */ - // protected function indexFormat($data) - // { - // if (isset($data['created_at'])) - // $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); - // if (isset($data['updated_at'])) - // $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); - // return $data; - // } + protected function indexFormat($data) + { + if (isset($data['created_at'])) $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + if (isset($data['eta_ship_date'])) $data['eta_ship_date'] = date('Y-m-d', $data['eta_ship_date']); + if (isset($data['biz_date'])) $data['biz_date'] = date('Y-m-d', $data['biz_date']); + if (isset($data['latest_arrival_date'])) $data['latest_arrival_date'] = date('Y-m-d', $data['latest_arrival_date']); + return $data; + } + protected function showFormat($data) + { + if (isset($data['created_at'])) $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']); + if (isset($data['updated_at'])) $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']); + if (isset($data['eta_ship_date'])) $data['eta_ship_date'] = date('Y-m-d', $data['eta_ship_date']); + if (isset($data['biz_date'])) $data['biz_date'] = date('Y-m-d', $data['biz_date']); + if (isset($data['latest_arrival_date'])) $data['latest_arrival_date'] = date('Y-m-d', $data['latest_arrival_date']); + + if (!empty($data['contract'])) { + $data['contract_url'] = (new \app\common\service\OssService)->generatePrivateUrl($data['contract']); + } + + if (count($data['moneyValues']) > 0) { + foreach ($data['moneyValues'] as $mvs) { + $data['cif_total_amount'] = [ + 'exchange_rate' => $mvs['exchange_rate'], + 'amount' => $mvs['amount'], + 'currency' => $mvs['currency'], + 'id' => $mvs['id'] + ]; + } + } + unset($data['moneyValues']); + + foreach ($data['items'] as &$item) { + foreach ($item['moneyValues'] as $mv) { + $item[$mv['field']] = [ + 'exchange_rate' => $mv['exchange_rate'], + 'amount' => $mv['amount'], + 'currency' => $mv['currency'], + 'id' => $mv['id'] + ]; + } + unset($item['moneyValues']); + + foreach ($item['options'] as $opt) { + $item['modify_list'][] = [ + "id" => $opt['id'], + "order_item_id" => $opt['order_item_id'], + "option_id" => $opt['option_id'], + "option_name_snapshot" => $opt['option_name_snapshot'], + "option_price" => $opt['option_price'] + ]; + } + unset($item['options']); + } + unset($item); + + + foreach ($data['accounts'] as $acc) { + $data['account_list'][] = [ + "id" => $acc['id'], + "order_id" => $acc['order_id'], + "seq_no" => $acc['seq_no'], + "remit_country_id" => $acc['remit_country_id'], + "remit_country_name_snapshot" => $acc['remit_country_name_snapshot'], + "remit_date" => date('Y-m-d', $acc['remit_date']), + "currency_code" => $acc['currency_code'], + "currency_name_snapshot" => $acc['currency_name_snapshot'], + "account_id" => $acc['account_id'], + "account_name_snapshot" => $acc['account_name_snapshot'], + "remitter_name" => $acc['remitter_name'], + "amount" => $acc['amount'] + ]; + } + unset($data['accounts']); + + $data['veh_list'] = $data['items']; + unset($data['items']); + return $data; + } + + + + public function print(Request $request) + { + try { + $raw = $this->service->servicePrint($request); + return $this->success($raw); + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } + - - public function index(Request $request) + public function desc(Request $request) { $data = [ diff --git a/composer.json b/composer.json index c474010..fcc2089 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,8 @@ "workerman/crontab": "^1.0", "phpoffice/phpspreadsheet": "^4.5", "phpmailer/phpmailer": "^6.10", - "webman/gateway-worker": "^1.0" + "webman/gateway-worker": "^1.0", + "mpdf/mpdf": "^8.2" }, "suggest": { "ext-event": "For better performance. " diff --git a/composer.lock b/composer.lock index 7d41f97..2272db8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7c70d8c8bda8a5098b8baed463db8839", + "content-hash": "68e74d2aef3a2f7f8163bfaf65431cb2", "packages": [ { "name": "aliyuncs/oss-sdk-php", @@ -2890,6 +2890,239 @@ ], "time": "2024-11-12T12:43:37+00:00" }, + { + "name": "mpdf/mpdf", + "version": "v8.2.6", + "source": { + "type": "git", + "url": "https://github.com/mpdf/mpdf.git", + "reference": "dd30e3b01061cf8dfe65e7041ab4cc46d8ebdd44" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mpdf/mpdf/zipball/dd30e3b01061cf8dfe65e7041ab4cc46d8ebdd44", + "reference": "dd30e3b01061cf8dfe65e7041ab4cc46d8ebdd44", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "ext-mbstring": "*", + "mpdf/psr-http-message-shim": "^1.0 || ^2.0", + "mpdf/psr-log-aware-trait": "^2.0 || ^3.0", + "myclabs/deep-copy": "^1.7", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": "^5.6 || ^7.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "setasign/fpdi": "^2.1" + }, + "require-dev": { + "mockery/mockery": "^1.3.0", + "mpdf/qrcode": "^1.1.0", + "squizlabs/php_codesniffer": "^3.5.0", + "tracy/tracy": "~2.5", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "ext-bcmath": "Needed for generation of some types of barcodes", + "ext-xml": "Needed mainly for SVG manipulation", + "ext-zlib": "Needed for compression of embedded resources, such as fonts" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Mpdf\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-only" + ], + "authors": [ + { + "name": "Matěj Humpál", + "role": "Developer, maintainer" + }, + { + "name": "Ian Back", + "role": "Developer (retired)" + } + ], + "description": "PHP library generating PDF files from UTF-8 encoded HTML", + "homepage": "https://mpdf.github.io", + "keywords": [ + "pdf", + "php", + "utf-8" + ], + "support": { + "docs": "https://mpdf.github.io", + "issues": "https://github.com/mpdf/mpdf/issues", + "source": "https://github.com/mpdf/mpdf" + }, + "funding": [ + { + "url": "https://www.paypal.me/mpdf", + "type": "custom" + } + ], + "time": "2025-08-18T08:51:51+00:00" + }, + { + "name": "mpdf/psr-http-message-shim", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/mpdf/psr-http-message-shim.git", + "reference": "f25a0153d645e234f9db42e5433b16d9b113920f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mpdf/psr-http-message-shim/zipball/f25a0153d645e234f9db42e5433b16d9b113920f", + "reference": "f25a0153d645e234f9db42e5433b16d9b113920f", + "shasum": "" + }, + "require": { + "psr/http-message": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Mpdf\\PsrHttpMessageShim\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Dorison", + "email": "mark@chromatichq.com" + }, + { + "name": "Kristofer Widholm", + "email": "kristofer@chromatichq.com" + }, + { + "name": "Nigel Cunningham", + "email": "nigel.cunningham@technocrat.com.au" + } + ], + "description": "Shim to allow support of different psr/message versions.", + "support": { + "issues": "https://github.com/mpdf/psr-http-message-shim/issues", + "source": "https://github.com/mpdf/psr-http-message-shim/tree/v2.0.1" + }, + "time": "2023-10-02T14:34:03+00:00" + }, + { + "name": "mpdf/psr-log-aware-trait", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/mpdf/psr-log-aware-trait.git", + "reference": "a633da6065e946cc491e1c962850344bb0bf3e78" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mpdf/psr-log-aware-trait/zipball/a633da6065e946cc491e1c962850344bb0bf3e78", + "reference": "a633da6065e946cc491e1c962850344bb0bf3e78", + "shasum": "" + }, + "require": { + "psr/log": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Mpdf\\PsrLogAwareTrait\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Dorison", + "email": "mark@chromatichq.com" + }, + { + "name": "Kristofer Widholm", + "email": "kristofer@chromatichq.com" + } + ], + "description": "Trait to allow support of different psr/log versions.", + "support": { + "issues": "https://github.com/mpdf/psr-log-aware-trait/issues", + "source": "https://github.com/mpdf/psr-log-aware-trait/tree/v3.0.0" + }, + "time": "2023-05-03T06:19:36+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, { "name": "nesbot/carbon", "version": "3.10.0", @@ -3975,6 +4208,78 @@ }, "time": "2019-03-08T08:55:37+00:00" }, + { + "name": "setasign/fpdi", + "version": "v2.6.4", + "source": { + "type": "git", + "url": "https://github.com/Setasign/FPDI.git", + "reference": "4b53852fde2734ec6a07e458a085db627c60eada" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Setasign/FPDI/zipball/4b53852fde2734ec6a07e458a085db627c60eada", + "reference": "4b53852fde2734ec6a07e458a085db627c60eada", + "shasum": "" + }, + "require": { + "ext-zlib": "*", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "setasign/tfpdf": "<1.31" + }, + "require-dev": { + "phpunit/phpunit": "^7", + "setasign/fpdf": "~1.8.6", + "setasign/tfpdf": "~1.33", + "squizlabs/php_codesniffer": "^3.5", + "tecnickcom/tcpdf": "^6.8" + }, + "suggest": { + "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured." + }, + "type": "library", + "autoload": { + "psr-4": { + "setasign\\Fpdi\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Slabon", + "email": "jan.slabon@setasign.com", + "homepage": "https://www.setasign.com" + }, + { + "name": "Maximilian Kresse", + "email": "maximilian.kresse@setasign.com", + "homepage": "https://www.setasign.com" + } + ], + "description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.", + "homepage": "https://www.setasign.com/fpdi", + "keywords": [ + "fpdf", + "fpdi", + "pdf" + ], + "support": { + "issues": "https://github.com/Setasign/FPDI/issues", + "source": "https://github.com/Setasign/FPDI/tree/v2.6.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/setasign/fpdi", + "type": "tidelift" + } + ], + "time": "2025-08-05T09:57:14+00:00" + }, { "name": "shopwwi/filesystem-oss", "version": "v1.0.3", diff --git a/config/route.php b/config/route.php index b1eb26e..e4248cd 100644 --- a/config/route.php +++ b/config/route.php @@ -289,6 +289,14 @@ Route::group('/admin', function () { Route::any('/destroy', [app\admin\controller\SysPortController::class, 'destroy']); }); + // 审批 + Route::group('/approval', function () { + Route::any('/list', [app\admin\controller\ApprovalFlowsController::class, 'index']); + Route::any('/show', [app\admin\controller\ApprovalFlowsController::class, 'show']); + Route::any('/store', [app\admin\controller\ApprovalFlowsController::class, 'store']); + Route::any('/update', [app\admin\controller\ApprovalFlowsController::class, 'update']); + Route::any('/destroy', [app\admin\controller\ApprovalFlowsController::class, 'destroy']); + }); }); @@ -358,7 +366,13 @@ Route::group('/crm', function () { Route::any('/list', [app\crm\controller\StockOrdersController::class, 'index']); Route::any('/show', [app\crm\controller\StockOrdersController::class, 'show']); Route::any('/store', [app\crm\controller\StockOrdersController::class, 'store']); + Route::any('/print', [app\crm\controller\StockOrdersController::class, 'print']); // Route::any('/update', [app\crm\controller\StockOrdersController::class, 'update']); // Route::any('/destroy', [app\crm\controller\StockOrdersController::class, 'destroy']); }); + + // Route::group('/pdf', function () { + // Route::any('/bx/{id}', [app\common\utils\pdf\controller\DocumentController::class, 'downloadReimbursementPdf']); + // Route::any('/ckbx/{id}', [app\common\utils\pdf\controller\DocumentController::class, 'viewReimbursementPdf']); + // }); }); \ No newline at end of file diff --git a/public/fonts/NotoSansSC-Bold.ttf b/public/fonts/NotoSansSC-Bold.ttf new file mode 100644 index 0000000..b9010df Binary files /dev/null and b/public/fonts/NotoSansSC-Bold.ttf differ diff --git a/public/fonts/NotoSansSC-Regular.ttf b/public/fonts/NotoSansSC-Regular.ttf new file mode 100644 index 0000000..4d4cadb Binary files /dev/null and b/public/fonts/NotoSansSC-Regular.ttf differ