回筐逻辑修改
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -182,7 +182,7 @@ class OrderController extends BaseMiniController
|
||||
$previewMap = [];
|
||||
$flatItems = [];
|
||||
foreach ($paginator->getCollection() as $order) {
|
||||
foreach ($order->items as $item) {
|
||||
foreach ($order->items->take(3) as $item) {
|
||||
$flatItems[] = [
|
||||
'order_id' => $order->id,
|
||||
'product_name' => $item->product_name,
|
||||
|
||||
@@ -2,124 +2,41 @@
|
||||
|
||||
namespace App\Http\Controllers\Recon;
|
||||
|
||||
use App\Exceptions\RepositoryException;
|
||||
use App\Http\Requests\Recon\ContainerReturnFormRequest;
|
||||
use App\Models\ContainerReturnModel;
|
||||
use App\Models\StoreModel;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||
use Modules\Common\Http\Controllers\BaseController;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 回筐记录(压筐=生成账单自动写入只读;回筐=门店退回手动登记,扣减门店待回数量)
|
||||
* 压回筐记录(周转筐/托盘跟账单走,生成账单时自动写入完整快照,只读)
|
||||
*
|
||||
* 数量正数=压筐(附加金额),负数=回筐(抵扣金额)
|
||||
*/
|
||||
#[RequestAttribute('/recon/container-return', 'recon.containerReturn')]
|
||||
class ContainerReturnController extends BaseController
|
||||
{
|
||||
protected array $searchField = [
|
||||
'store_id' => '=',
|
||||
'type' => '=',
|
||||
'return_date' => 'betweenDate',
|
||||
'bill_id' => '=',
|
||||
'created_at' => 'betweenDate',
|
||||
];
|
||||
|
||||
/** 回筐记录列表 */
|
||||
/** 压回筐记录列表 */
|
||||
#[GetRoute(authorize: 'query')]
|
||||
public function query(Request $request): JsonResponse
|
||||
{
|
||||
$params = $request->all();
|
||||
$pageSize = $params['pageSize'] ?? 10;
|
||||
$data = $this->buildSearch($params, ContainerReturnModel::query()->with([
|
||||
'store:id,name,pending_box_num,pending_tray_num',
|
||||
'bill:id,bill_no',
|
||||
'store:id,name',
|
||||
'bill:id,bill_no,bill_date',
|
||||
'operator:id,nickname',
|
||||
]))
|
||||
->orderBy('return_date', 'desc')
|
||||
->orderBy('id', 'desc')
|
||||
->paginate($pageSize)
|
||||
->toArray();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回筐登记:门店退回周转筐/托盘,扣减门店待回数量(超回拒绝)
|
||||
* @throws Throwable
|
||||
*/
|
||||
#[PostRoute(authorize: 'create')]
|
||||
public function create(ContainerReturnFormRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->validated();
|
||||
$boxNum = (int) $data['box_num'];
|
||||
$trayNum = (int) $data['tray_num'];
|
||||
if ($boxNum === 0 && $trayNum === 0) {
|
||||
throw new RepositoryException('周转筐与周转托盘数量不能同时为 0');
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($data, $boxNum, $trayNum, $request) {
|
||||
$store = StoreModel::query()->lockForUpdate()->find((int) $data['store_id']);
|
||||
if (empty($store)) {
|
||||
throw new RepositoryException('门店不存在');
|
||||
}
|
||||
if ($boxNum > (int) $store->pending_box_num) {
|
||||
throw new RepositoryException(
|
||||
'回筐数量超过该门店待回筐数量(当前待回 ' . (int) $store->pending_box_num . ' 个)'
|
||||
);
|
||||
}
|
||||
if ($trayNum > (int) $store->pending_tray_num) {
|
||||
throw new RepositoryException(
|
||||
'回托盘数量超过该门店待回托盘数量(当前待回 ' . (int) $store->pending_tray_num . ' 个)'
|
||||
);
|
||||
}
|
||||
|
||||
$store->pending_box_num = (int) $store->pending_box_num - $boxNum;
|
||||
$store->pending_tray_num = (int) $store->pending_tray_num - $trayNum;
|
||||
$store->save();
|
||||
|
||||
ContainerReturnModel::create([
|
||||
'store_id' => $store->id,
|
||||
'bill_id' => 0,
|
||||
'type' => ContainerReturnModel::TYPE_RETURN,
|
||||
'box_num' => $boxNum,
|
||||
'tray_num' => $trayNum,
|
||||
'return_date' => $data['return_date'],
|
||||
'operator_id' => (int) $request->user()->id,
|
||||
'remark' => (string) ($data['remark'] ?? ''),
|
||||
]);
|
||||
|
||||
return $this->success([], '回筐已登记');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除回筐记录(仅手动登记的回筐记录可删,删除后恢复门店待回数量;压筐记录随账单生成不允许删除)
|
||||
* @throws Throwable
|
||||
*/
|
||||
#[DeleteRoute(route: '/{id}', authorize: 'delete', where: ['id' => '[0-9]+'])]
|
||||
public function delete(int $id): JsonResponse
|
||||
{
|
||||
$record = ContainerReturnModel::find($id);
|
||||
if (empty($record)) {
|
||||
throw new RepositoryException('回筐记录不存在');
|
||||
}
|
||||
if ($record->type !== ContainerReturnModel::TYPE_RETURN) {
|
||||
throw new RepositoryException('压筐记录由账单生成,不允许删除');
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($record) {
|
||||
$store = StoreModel::query()->lockForUpdate()->find($record->store_id);
|
||||
if ($store !== null) {
|
||||
$store->pending_box_num = (int) $store->pending_box_num + (int) $record->box_num;
|
||||
$store->pending_tray_num = (int) $store->pending_tray_num + (int) $record->tray_num;
|
||||
$store->save();
|
||||
}
|
||||
$record->delete();
|
||||
|
||||
return $this->success([], '回筐记录已删除,门店待回数量已恢复');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ namespace App\Http\Requests\Purchase;
|
||||
use Modules\Common\Http\Requests\BaseFormRequest;
|
||||
|
||||
/**
|
||||
* 采购单生成账单 验证(按门店提交配送费/周转筐/托盘数量,金额由系统汇总不可修改)
|
||||
* 采购单生成账单 验证(按门店提交配送费/周转筐/托盘数量,金额由系统汇总不可修改;
|
||||
* 筐/托盘数量正数=压筐附加金额,负数=回筐抵扣金额)
|
||||
*/
|
||||
class PurchaseBillGenerateRequest extends BaseFormRequest
|
||||
{
|
||||
@@ -17,8 +18,8 @@ class PurchaseBillGenerateRequest extends BaseFormRequest
|
||||
'stores' => 'required|array|min:1',
|
||||
'stores.*.store_id' => 'required|integer|distinct',
|
||||
'stores.*.delivery_fee' => 'required|numeric|min:0',
|
||||
'stores.*.box_num' => 'required|integer|min:0',
|
||||
'stores.*.tray_num' => 'required|integer|min:0',
|
||||
'stores.*.box_num' => 'required|integer',
|
||||
'stores.*.tray_num' => 'required|integer',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -35,11 +36,9 @@ class PurchaseBillGenerateRequest extends BaseFormRequest
|
||||
'stores.*.delivery_fee.numeric' => '配送费格式错误',
|
||||
'stores.*.delivery_fee.min' => '配送费不能小于 0',
|
||||
'stores.*.box_num.required' => '周转筐数量不能为空',
|
||||
'stores.*.box_num.integer' => '周转筐数量必须为整数',
|
||||
'stores.*.box_num.min' => '周转筐数量不能小于 0',
|
||||
'stores.*.box_num.integer' => '周转筐数量必须为整数(正数=压筐,负数=回筐)',
|
||||
'stores.*.tray_num.required' => '周转托盘数量不能为空',
|
||||
'stores.*.tray_num.integer' => '周转托盘数量必须为整数',
|
||||
'stores.*.tray_num.min' => '周转托盘数量不能小于 0',
|
||||
'stores.*.tray_num.integer' => '周转托盘数量必须为整数(正数=压筐,负数=回筐)',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Recon;
|
||||
|
||||
use Modules\Common\Http\Requests\BaseFormRequest;
|
||||
|
||||
/**
|
||||
* 回筐登记 验证(门店退回周转筐/托盘,扣减门店待回数量)
|
||||
*/
|
||||
class ContainerReturnFormRequest extends BaseFormRequest
|
||||
{
|
||||
protected $stopOnFirstFailure = true;
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'store_id' => 'required|integer|exists:store,id',
|
||||
'box_num' => 'required|integer|min:0',
|
||||
'tray_num' => 'required|integer|min:0',
|
||||
'return_date' => 'required|date_format:Y-m-d',
|
||||
'remark' => 'nullable|string|max:255',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'store_id.required' => '请选择门店',
|
||||
'store_id.exists' => '门店不存在',
|
||||
'box_num.required' => '周转筐数量不能为空',
|
||||
'box_num.integer' => '周转筐数量必须为整数',
|
||||
'box_num.min' => '周转筐数量不能小于 0',
|
||||
'tray_num.required' => '周转托盘数量不能为空',
|
||||
'tray_num.integer' => '周转托盘数量必须为整数',
|
||||
'tray_num.min' => '周转托盘数量不能小于 0',
|
||||
'return_date.required' => '请选择退回日期',
|
||||
'return_date.date_format' => '退回日期格式为 Y-m-d',
|
||||
'remark.max' => '备注超过最大长度',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -8,44 +8,36 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Modules\SystemUser\Models\SysUserModel;
|
||||
|
||||
/**
|
||||
* 门店回筐记录模型(压筐=生成账单时自动写入并累加门店待回;回筐=门店退回手动登记并扣减待回)
|
||||
* 门店压回筐记录模型(周转筐/托盘跟账单走,生成账单时写入完整快照)
|
||||
*
|
||||
* 数量正数=压筐(附加金额),负数=回筐(抵扣金额);数量为 0 不写记录
|
||||
*/
|
||||
class ContainerReturnModel extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/** 类型:压筐(账单生成压出,系统写入只读) */
|
||||
public const int TYPE_PRESS = 1;
|
||||
/** 类型:回筐(门店退回,后台手动登记) */
|
||||
public const int TYPE_RETURN = 2;
|
||||
|
||||
/** 类型中文名 */
|
||||
public const array TYPE_NAMES = [
|
||||
self::TYPE_PRESS => '压筐',
|
||||
self::TYPE_RETURN => '回筐',
|
||||
];
|
||||
|
||||
protected $table = 'container_return';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
protected $fillable = [
|
||||
'store_id',
|
||||
'bill_id',
|
||||
'type',
|
||||
'box_num',
|
||||
'tray_num',
|
||||
'return_date',
|
||||
'box_price',
|
||||
'tray_price',
|
||||
'amount',
|
||||
'operator_id',
|
||||
'remark',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'store_id' => 'integer',
|
||||
'bill_id' => 'integer',
|
||||
'type' => 'integer',
|
||||
'box_num' => 'integer',
|
||||
'tray_num' => 'integer',
|
||||
'return_date' => 'date:Y-m-d',
|
||||
'box_price' => 'decimal:2',
|
||||
'tray_price' => 'decimal:2',
|
||||
'amount' => 'decimal:2',
|
||||
'operator_id' => 'integer',
|
||||
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||
];
|
||||
@@ -59,7 +51,7 @@ class ContainerReturnModel extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联账单(压筐记录)
|
||||
* 关联账单
|
||||
*/
|
||||
public function bill(): BelongsTo
|
||||
{
|
||||
|
||||
@@ -38,8 +38,6 @@ class StoreModel extends Model
|
||||
protected $casts = [
|
||||
'level_id' => 'integer',
|
||||
'payment_cycle_days' => 'integer',
|
||||
'pending_box_num' => 'integer',
|
||||
'pending_tray_num' => 'integer',
|
||||
'total_purchase_amount' => 'decimal:2',
|
||||
'status' => 'integer',
|
||||
];
|
||||
|
||||
@@ -18,8 +18,10 @@ use Throwable;
|
||||
* 流程(事务内):
|
||||
* 1. 锁定采购单全部有效订单,按门店分组,校验提交门店完整覆盖
|
||||
* 2. 商品金额 = 门店订单商品金额汇总(快照,生成后不可修改)
|
||||
* 3. 附加金额 = 周转筐数量×筐单价 + 托盘数量×托盘单价(单价取站点配置快照)
|
||||
* 3. 附加金额 = 周转筐数量×筐单价 + 托盘数量×托盘单价(单价取站点配置快照;
|
||||
* 数量正数=压筐附加金额,负数=回筐抵扣金额)
|
||||
* 4. 总金额 = 商品金额 + 配送费 + 附加金额;回写门店订单 bill_id 完成关联,订单状态置为已完成
|
||||
* 5. 压回筐记录:筐/托盘数量非 0 时写入完整快照(数量/单价/金额均可为负)
|
||||
*/
|
||||
readonly class BillGenerateService
|
||||
{
|
||||
@@ -122,22 +124,19 @@ readonly class BillGenerateService
|
||||
->whereIn('order_id', $storeOrders->pluck('id'))
|
||||
->update(['bill_id' => $bill->id]);
|
||||
|
||||
// 压筐:累加门店待回筐/托盘(行锁防并发),并写入压筐记录
|
||||
$store = StoreModel::query()->lockForUpdate()->find((int) $storeId);
|
||||
if ($store !== null) {
|
||||
$store->pending_box_num = (int) $store->pending_box_num + (int) $row['box_num'];
|
||||
$store->pending_tray_num = (int) $store->pending_tray_num + (int) $row['tray_num'];
|
||||
$store->save();
|
||||
}
|
||||
// 压回筐记录:数量为 0 不写记录;正数=压筐附加金额,负数=回筐抵扣金额
|
||||
if ((int) $row['box_num'] !== 0 || (int) $row['tray_num'] !== 0) {
|
||||
ContainerReturnModel::create([
|
||||
'store_id' => (int) $storeId,
|
||||
'bill_id' => $bill->id,
|
||||
'type' => ContainerReturnModel::TYPE_PRESS,
|
||||
'box_num' => (int) $row['box_num'],
|
||||
'tray_num' => (int) $row['tray_num'],
|
||||
'return_date' => $billDate,
|
||||
'box_price' => bcadd($boxPrice, '0', 2),
|
||||
'tray_price' => bcadd($trayPrice, '0', 2),
|
||||
'amount' => $addedAmount,
|
||||
'operator_id' => $operatorId,
|
||||
]);
|
||||
}
|
||||
|
||||
$bills[] = $bill;
|
||||
}
|
||||
|
||||
@@ -60,8 +60,6 @@ return new class extends Migration
|
||||
$table->string('phone', 20)->default('')->comment('联系电话');
|
||||
$table->string('address', 255)->default('')->comment('门店地址');
|
||||
$table->integer('payment_cycle_days')->default(0)->comment('回款周期(天)');
|
||||
$table->integer('pending_box_num')->default(0)->comment('待回筐数量(生成账单压筐累加,回筐登记扣减)');
|
||||
$table->integer('pending_tray_num')->default(0)->comment('待回托盘数量');
|
||||
$table->decimal('total_purchase_amount', 12, 2)->default(0)->comment('总采购金额(只统计商品金额,账单支付后累加)');
|
||||
$table->integer('status')->default(1)->comment('状态(1正常 0停用)');
|
||||
$table->string('remark', 255)->nullable()->default('')->comment('备注');
|
||||
|
||||
@@ -8,7 +8,8 @@ return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
* 门店回筐台账:压筐(生成账单时自动写入)+ 回筐(门店退回,后台手动登记)
|
||||
* 门店压回筐记录:周转筐/托盘跟账单走,生成账单时写入完整快照;
|
||||
* 数量正数=压筐(附加金额),负数=回筐(抵扣金额),数量为 0 不写记录
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
@@ -16,17 +17,17 @@ return new class extends Migration
|
||||
Schema::create('container_return', function (Blueprint $table) {
|
||||
$table->increments('id')->comment('记录ID');
|
||||
$table->integer('store_id')->comment('门店ID');
|
||||
$table->integer('bill_id')->default(0)->comment('关联账单ID(0=手动回筐登记)');
|
||||
$table->integer('type')->comment('类型(1压筐-账单生成 2回筐-退回登记)');
|
||||
$table->integer('box_num')->default(0)->comment('周转筐数量');
|
||||
$table->integer('tray_num')->default(0)->comment('周转托盘数量');
|
||||
$table->date('return_date')->comment('记录日期(压筐=账单日期,回筐=退回日期)');
|
||||
$table->integer('bill_id')->comment('关联账单ID');
|
||||
$table->integer('box_num')->default(0)->comment('压(回)筐数量(正数=压筐,负数=回筐)');
|
||||
$table->integer('tray_num')->default(0)->comment('压(回)托盘数量(正数=压筐,负数=回筐)');
|
||||
$table->decimal('box_price', 10, 2)->default(0)->comment('周转筐单价(生成账单时快照)');
|
||||
$table->decimal('tray_price', 10, 2)->default(0)->comment('周转托盘单价(生成账单时快照)');
|
||||
$table->decimal('amount', 10, 2)->default(0)->comment('抵扣(附加)金额 = 筐×筐单价 + 托盘×托盘单价(正=压筐附加,负=回筐抵扣)');
|
||||
$table->integer('operator_id')->default(0)->comment('操作人(后台系统用户ID)');
|
||||
$table->string('remark', 255)->default('')->comment('备注');
|
||||
$table->timestamps();
|
||||
$table->index(['store_id', 'type'], 'container_return_store_type_index');
|
||||
$table->index(['store_id'], 'container_return_store_index');
|
||||
$table->index(['bill_id'], 'container_return_bill_index');
|
||||
$table->comment('门店回筐记录表(压筐=生成账单压出,回筐=门店退回登记)');
|
||||
$table->comment('门店压回筐记录表(随账单生成:正数=压筐附加金额,负数=回筐抵扣金额)');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,8 +223,6 @@ class PermissionSeeder extends Seeder
|
||||
'path' => '/recon/container-return',
|
||||
'children' => [
|
||||
['type' => 'rule', 'key' => 'recon.containerReturn.query', 'name' => '查询'],
|
||||
['type' => 'rule', 'key' => 'recon.containerReturn.create', 'name' => '回筐登记'],
|
||||
['type' => 'rule', 'key' => 'recon.containerReturn.delete', 'name' => '删除'],
|
||||
],
|
||||
],
|
||||
[
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\BillModel;
|
||||
use App\Models\ContainerReturnModel;
|
||||
use App\Models\CustomerLevelModel;
|
||||
use App\Models\ProductModel;
|
||||
use App\Models\ProductPriceModel;
|
||||
use App\Models\PurchaseOrderModel;
|
||||
use App\Models\StoreModel;
|
||||
use App\Models\StoreOrderModel;
|
||||
use App\Models\UserModel;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\SystemTool\Services\SysSiteConfigService;
|
||||
|
||||
/**
|
||||
* 压回筐记录:周转筐/托盘跟账单走(生成账单时写入完整快照,只读);
|
||||
* 数量正数=压筐(附加金额),负数=回筐(抵扣金额),数量为 0 不写记录;
|
||||
* 门店表不再记录待回数量
|
||||
*/
|
||||
class ContainerReturnTest extends ProcurementTestCase
|
||||
{
|
||||
/** 注入站点配置:筐单价 5.00、托盘单价 20.00 */
|
||||
private function seedContainerConfig(): void
|
||||
{
|
||||
DB::table('sys_site_config_group')->insert([
|
||||
'id' => 100,
|
||||
'title' => '业务配置',
|
||||
'key' => 'services',
|
||||
'remark' => '',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
DB::table('sys_site_config_items')->insert([
|
||||
[
|
||||
'group_id' => 100, 'key' => 'box_amount', 'title' => '周转筐金额', 'describe' => '',
|
||||
'values' => '5.00', 'type' => 'InputNumber', 'options' => null, 'props' => null, 'sort' => 0,
|
||||
'created_at' => now(), 'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'group_id' => 100, 'key' => 'tray_amount', 'title' => '周转托盘金额', 'describe' => '',
|
||||
'values' => '20.00', 'type' => 'InputNumber', 'options' => null, 'props' => null, 'sort' => 1,
|
||||
'created_at' => now(), 'updated_at' => now(),
|
||||
],
|
||||
]);
|
||||
SysSiteConfigService::refreshSiteConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 完整业务链造账单(商品 5.00 × 4 = 20.00,配送费 0),按指定筐/托盘数量生成
|
||||
*
|
||||
* @return array{0: StoreModel, 1: BillModel}
|
||||
*/
|
||||
private function makeBillWithContainers(int $boxNum, int $trayNum): array
|
||||
{
|
||||
$level = CustomerLevelModel::factory()->create();
|
||||
$store = StoreModel::factory()->create(['level_id' => $level->id]);
|
||||
$product = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON]);
|
||||
ProductPriceModel::factory()->create([
|
||||
'product_id' => $product->id,
|
||||
'level_id' => $level->id,
|
||||
'price' => '5.00',
|
||||
]);
|
||||
$user = UserModel::factory()->forStore($store->id)->create();
|
||||
|
||||
$this->actingAsMiniUser($user);
|
||||
$this->postJson('/mini/order', ['items' => [['product_id' => $product->id, 'quantity' => 4]]])
|
||||
->assertJsonPath('success', true);
|
||||
$order = StoreOrderModel::where('store_id', $store->id)->first();
|
||||
|
||||
$this->actingAsSysUser();
|
||||
$this->putJson("/order/store/{$order->id}/status", ['status' => StoreOrderModel::STATUS_SUMMARIZED])
|
||||
->assertJsonPath('success', true);
|
||||
$this->postJson('/purchase/order/generate', ['purchase_date' => now()->toDateString()])
|
||||
->assertJsonPath('success', true);
|
||||
$purchase = PurchaseOrderModel::first();
|
||||
$this->putJson("/purchase/order/{$purchase->id}/finish")->assertJsonPath('success', true);
|
||||
$this->postJson("/purchase/order/{$purchase->id}/bill", [
|
||||
'stores' => [[
|
||||
'store_id' => $store->id,
|
||||
'delivery_fee' => 0,
|
||||
'box_num' => $boxNum,
|
||||
'tray_num' => $trayNum,
|
||||
]],
|
||||
])->assertJsonPath('success', true);
|
||||
|
||||
return [$store, BillModel::where('store_id', $store->id)->first()];
|
||||
}
|
||||
|
||||
/** 压筐(正数):附加金额增加账单总额,写入完整快照记录 */
|
||||
public function test_press_containers_adds_amount_and_writes_record(): void
|
||||
{
|
||||
$this->seedContainerConfig();
|
||||
[$store, $bill] = $this->makeBillWithContainers(3, 2);
|
||||
|
||||
// 附加 = 3×5 + 2×20 = 55;总额 = 20 + 55 = 75
|
||||
$this->assertSame('55.00', (string) $bill->added_amount);
|
||||
$this->assertSame('75.00', (string) $bill->total_amount);
|
||||
|
||||
$record = ContainerReturnModel::where('bill_id', $bill->id)->first();
|
||||
$this->assertNotNull($record);
|
||||
$this->assertSame($store->id, $record->store_id);
|
||||
$this->assertSame(3, $record->box_num);
|
||||
$this->assertSame(2, $record->tray_num);
|
||||
$this->assertSame('5.00', (string) $record->box_price, '筐单价快照');
|
||||
$this->assertSame('20.00', (string) $record->tray_price, '托盘单价快照');
|
||||
$this->assertSame('55.00', (string) $record->amount);
|
||||
}
|
||||
|
||||
/** 回筐(负数):抵扣账单金额,记录为负 */
|
||||
public function test_return_containers_deducts_amount(): void
|
||||
{
|
||||
$this->seedContainerConfig();
|
||||
[, $bill] = $this->makeBillWithContainers(-2, 0);
|
||||
|
||||
// 附加 = -2×5 + 0 = -10;总额 = 20 - 10 = 10
|
||||
$this->assertSame('-10.00', (string) $bill->added_amount);
|
||||
$this->assertSame('10.00', (string) $bill->total_amount);
|
||||
|
||||
$record = ContainerReturnModel::where('bill_id', $bill->id)->first();
|
||||
$this->assertNotNull($record);
|
||||
$this->assertSame(-2, $record->box_num);
|
||||
$this->assertSame(0, $record->tray_num);
|
||||
$this->assertSame('-10.00', (string) $record->amount);
|
||||
}
|
||||
|
||||
/** 混合:筐回(负)+ 托盘压(正),金额按代数和计 */
|
||||
public function test_mixed_sign_containers(): void
|
||||
{
|
||||
$this->seedContainerConfig();
|
||||
[, $bill] = $this->makeBillWithContainers(-3, 1);
|
||||
|
||||
// 附加 = -3×5 + 1×20 = 5;总额 = 20 + 5 = 25
|
||||
$this->assertSame('5.00', (string) $bill->added_amount);
|
||||
$this->assertSame('25.00', (string) $bill->total_amount);
|
||||
|
||||
$record = ContainerReturnModel::where('bill_id', $bill->id)->first();
|
||||
$this->assertNotNull($record);
|
||||
$this->assertSame(-3, $record->box_num);
|
||||
$this->assertSame(1, $record->tray_num);
|
||||
$this->assertSame('5.00', (string) $record->amount);
|
||||
}
|
||||
|
||||
/** 数量为 0:不写压回筐记录 */
|
||||
public function test_zero_containers_writes_no_record(): void
|
||||
{
|
||||
$this->seedContainerConfig();
|
||||
[, $bill] = $this->makeBillWithContainers(0, 0);
|
||||
|
||||
$this->assertSame('20.00', (string) $bill->total_amount);
|
||||
$this->assertSame(0, ContainerReturnModel::where('bill_id', $bill->id)->count());
|
||||
}
|
||||
|
||||
/** 筐/托盘数量非整数 → 拒绝 */
|
||||
public function test_non_integer_containers_rejected(): void
|
||||
{
|
||||
$level = CustomerLevelModel::factory()->create();
|
||||
$store = StoreModel::factory()->create(['level_id' => $level->id]);
|
||||
$this->actingAsSysUser();
|
||||
$purchase = PurchaseOrderModel::factory()->create(['status' => PurchaseOrderModel::STATUS_COMPLETED]);
|
||||
|
||||
$this->postJson("/purchase/order/{$purchase->id}/bill", [
|
||||
'stores' => [[
|
||||
'store_id' => $store->id,
|
||||
'delivery_fee' => 0,
|
||||
'box_num' => 1.5,
|
||||
'tray_num' => 0,
|
||||
]],
|
||||
])->assertJsonPath('success', false);
|
||||
}
|
||||
|
||||
/** 压回筐记录只读:手动登记/删除端点已下线 */
|
||||
public function test_manual_create_and_delete_routes_removed(): void
|
||||
{
|
||||
$this->actingAsSysUser();
|
||||
|
||||
$this->postJson('/recon/container-return', [
|
||||
'store_id' => 1,
|
||||
'box_num' => 1,
|
||||
'tray_num' => 0,
|
||||
])->assertJsonPath('success', false);
|
||||
$this->deleteJson('/recon/container-return/1')->assertJsonPath('success', false);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,23 @@
|
||||
/** 门店回筐记录(压筐=生成账单自动写入只读;回筐=门店退回手动登记) */
|
||||
/** 门店压回筐记录(随账单生成:数量正数=压筐附加金额,负数=回筐抵扣金额) */
|
||||
export default interface IContainerReturn {
|
||||
id?: number;
|
||||
store_id?: number;
|
||||
/** 关联账单ID(0=手动回筐登记) */
|
||||
/** 关联账单ID */
|
||||
bill_id?: number;
|
||||
/** 类型:1压筐 2回筐 */
|
||||
type?: number;
|
||||
/** 压(回)筐数量(正数=压筐,负数=回筐) */
|
||||
box_num?: number;
|
||||
/** 压(回)托盘数量(正数=压筐,负数=回筐) */
|
||||
tray_num?: number;
|
||||
/** 记录日期(压筐=账单日期,回筐=退回日期) */
|
||||
return_date?: string;
|
||||
/** 周转筐单价(生成账单时快照) */
|
||||
box_price?: string;
|
||||
/** 周转托盘单价(生成账单时快照) */
|
||||
tray_price?: string;
|
||||
/** 抵扣(附加)金额(正=压筐附加,负=回筐抵扣) */
|
||||
amount?: string;
|
||||
operator_id?: number;
|
||||
remark?: string;
|
||||
created_at?: string;
|
||||
/** 列表接口附带 */
|
||||
store?: { id: number; name: string; pending_box_num?: number; pending_tray_num?: number } | null;
|
||||
bill?: { id: number; bill_no: string } | null;
|
||||
store?: { id: number; name: string } | null;
|
||||
bill?: { id: number; bill_no: string; bill_date?: string } | null;
|
||||
operator?: { id: number; nickname: string } | null;
|
||||
}
|
||||
|
||||
/** 回筐记录类型映射 */
|
||||
export const CONTAINER_TYPE_MAP: Record<number, { text: string; color: string }> = {
|
||||
1: { text: '压筐', color: 'processing' },
|
||||
2: { text: '回筐', color: 'success' },
|
||||
};
|
||||
|
||||
@@ -14,10 +14,6 @@ export default interface IStore {
|
||||
address?: string;
|
||||
/** 回款周期(天) */
|
||||
payment_cycle_days?: number;
|
||||
/** 待回筐数量(生成账单压筐累加,回筐登记扣减) */
|
||||
pending_box_num?: number;
|
||||
/** 待回托盘数量 */
|
||||
pending_tray_num?: number;
|
||||
/** 总采购金额(只统计商品金额,账单支付后累加) */
|
||||
total_purchase_amount?: string;
|
||||
status?: number;
|
||||
|
||||
@@ -52,8 +52,8 @@ const CustomerLevelPage: React.FC = () => {
|
||||
return (
|
||||
<Image
|
||||
src={url}
|
||||
width={60}
|
||||
height={60}
|
||||
width={30}
|
||||
height={30}
|
||||
style={{ objectFit: 'cover', borderRadius: 4 }}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -86,30 +86,6 @@ const StorePage: React.FC = () => {
|
||||
fieldProps: { min: 0, precision: 0 },
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '待回筐数量',
|
||||
dataIndex: 'pending_box_num',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) => (
|
||||
<Text strong type={Number(record.pending_box_num) > 0 ? 'warning' : undefined}>
|
||||
{record.pending_box_num ?? 0}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '待回托盘数量',
|
||||
dataIndex: 'pending_tray_num',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) => (
|
||||
<Text strong type={Number(record.pending_tray_num) > 0 ? 'warning' : undefined}>
|
||||
{record.pending_tray_num ?? 0}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '总采购金额',
|
||||
dataIndex: 'total_purchase_amount',
|
||||
|
||||
@@ -96,6 +96,7 @@ const SupplierPage: React.FC = () => {
|
||||
grid: true,
|
||||
colProps: { span: 12 },
|
||||
layout: 'vertical',
|
||||
rowProps: {gutter: 24}
|
||||
},
|
||||
modalProps: { width: 720 },
|
||||
};
|
||||
|
||||
@@ -329,10 +329,6 @@ const StoreOrderPage: React.FC = () => {
|
||||
{record.bill.pay_state_name ?? BILL_PAY_STATE_MAP[record.bill.pay_state ?? 0]?.text}
|
||||
</Tag>
|
||||
</div>
|
||||
<div>
|
||||
<Text type={'secondary'}>账单金额:</Text>
|
||||
<span className={'text-[red]'}>{record.bill.total_amount} ¥</span>
|
||||
</div>
|
||||
</Space>
|
||||
) : '-'
|
||||
},
|
||||
@@ -524,17 +520,6 @@ const StoreOrderPage: React.FC = () => {
|
||||
{detail.bill.pay_state_name ?? BILL_PAY_STATE_MAP[detail.bill.pay_state ?? 0]?.text}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="商品金额">¥{detail.bill.product_amount ?? '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="配送费">¥{detail.bill.delivery_fee ?? '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="附加金额">
|
||||
¥{detail.bill.added_amount ?? '0.00'}
|
||||
<Text type={'secondary'} className={'ml-1 text-[12px]'}>
|
||||
(筐{detail.bill.box_num ?? 0}/托盘{detail.bill.tray_num ?? 0})
|
||||
</Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="账单总金额">
|
||||
<Text strong className={'text-[red]'}>¥{detail.bill.total_amount}</Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="付款时间">{detail.bill.paid_at ?? '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="付款备注">{detail.bill.pay_remark || '-'}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
@@ -1094,7 +1094,7 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
{billPrepare && (
|
||||
<>
|
||||
<div className="py-2 text-gray-500">
|
||||
每个门店单独生成一张账单;商品金额由订单汇总不可修改,请填写各门店的配送费与周转筐/托盘数量。
|
||||
每个门店单独生成一张账单;商品金额由订单汇总不可修改,请填写各门店的配送费与周转筐/托盘数量(正数=压筐附加金额,负数=回筐抵扣金额)。
|
||||
{billAllGenerated ? '该采购单已全部生成账单,仅可查看。' : '生成后采购单中的全部订单将关联到对应门店账单。'}
|
||||
</div>
|
||||
<Form form={billForm} layout="vertical" onFinish={handleBillSave}>
|
||||
@@ -1105,8 +1105,8 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
<div className="w-40 shrink-0">门店</div>
|
||||
<div className="w-28 shrink-0 text-center">商品金额</div>
|
||||
<div className="w-32 shrink-0 text-center">配送费(元)</div>
|
||||
<div className="w-32 shrink-0 text-center">周转筐(¥{billPrepare.stores[0]?.box_price ?? '0.00'})</div>
|
||||
<div className="w-32 shrink-0 text-center">周转托盘(¥{billPrepare.stores[0]?.tray_price ?? '0.00'})</div>
|
||||
<div className="w-32 shrink-0 text-center">周转筐(¥{billPrepare.stores[0]?.box_price ?? '0.00'}/个)</div>
|
||||
<div className="w-32 shrink-0 text-center">周转托盘(¥{billPrepare.stores[0]?.tray_price ?? '0.00'}/个)</div>
|
||||
<div className="w-28 shrink-0 text-center">附加金额</div>
|
||||
<div className="flex-1 text-center">账单总金额</div>
|
||||
</div>
|
||||
@@ -1148,10 +1148,9 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
>
|
||||
<InputNumber
|
||||
className="w-full"
|
||||
min={0}
|
||||
precision={0}
|
||||
disabled={billed}
|
||||
placeholder="周转筐数量"
|
||||
placeholder="正压负回"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
@@ -1161,10 +1160,9 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
>
|
||||
<InputNumber
|
||||
className="w-full"
|
||||
min={0}
|
||||
precision={0}
|
||||
disabled={billed}
|
||||
placeholder="周转托盘数量"
|
||||
placeholder="正压负回"
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="w-28 shrink-0 text-center">
|
||||
|
||||
@@ -1,18 +1,40 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { message, Tag, Typography } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { Typography } from 'antd';
|
||||
import XinTable from '@/components/XinTable';
|
||||
import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings.ts';
|
||||
import type IContainerReturn from '@/domain/iContainerReturn.ts';
|
||||
import { CONTAINER_TYPE_MAP } from '@/domain/iContainerReturn.ts';
|
||||
import { getStoreOptions } from '@/api/customer/store.ts';
|
||||
import { Create } from '@/api/common/table.ts';
|
||||
import type IStore from '@/domain/iStore.ts';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
/** 有符号数量展示:正数=压筐(橙色 +N),负数=回筐(绿色 N),0 置灰 */
|
||||
const SignedNum: React.FC<{ value?: number }> = ({ value }) => {
|
||||
const num = Number(value ?? 0);
|
||||
if (num > 0) {
|
||||
return <Text strong type="warning">+{num}</Text>;
|
||||
}
|
||||
if (num < 0) {
|
||||
return <Text strong type="success">{num}</Text>;
|
||||
}
|
||||
return <Text type="secondary">0</Text>;
|
||||
};
|
||||
|
||||
/** 有符号金额展示:正=压筐附加(橙),负=回筐抵扣(绿) */
|
||||
const SignedAmount: React.FC<{ value?: string }> = ({ value }) => {
|
||||
const num = Number(value ?? 0);
|
||||
if (num > 0) {
|
||||
return <Text strong type="warning">+¥{num.toFixed(2)}</Text>;
|
||||
}
|
||||
if (num < 0) {
|
||||
return <Text strong type="success">-¥{Math.abs(num).toFixed(2)}</Text>;
|
||||
}
|
||||
return <Text type="secondary">¥0.00</Text>;
|
||||
};
|
||||
|
||||
/**
|
||||
* 回筐记录(压筐=生成账单时自动写入,只读;回筐=门店退回手动登记,删除后恢复门店待回数量)
|
||||
* 压回筐记录(周转筐/托盘跟账单走:生成账单时自动写入完整快照,只读)
|
||||
* 数量正数=压筐(附加金额),负数=回筐(抵扣金额)
|
||||
*/
|
||||
const ContainerReturnPage: React.FC = () => {
|
||||
const [stores, setStores] = useState<IStore[]>([]);
|
||||
@@ -26,7 +48,7 @@ const ContainerReturnPage: React.FC = () => {
|
||||
title: '门店',
|
||||
dataIndex: 'store_id',
|
||||
valueType: 'select',
|
||||
rules: [{ required: true, message: '请选择门店' }],
|
||||
hideInForm: true,
|
||||
fieldProps: {
|
||||
options: stores.map((s) => ({ label: s.name, value: s.id })),
|
||||
showSearch: true,
|
||||
@@ -34,81 +56,57 @@ const ContainerReturnPage: React.FC = () => {
|
||||
},
|
||||
render: (_, record) => record.store?.name ?? `门店#${record.store_id}`,
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
valueType: 'select',
|
||||
hideInForm: true,
|
||||
align: 'center',
|
||||
fieldProps: {
|
||||
options: Object.entries(CONTAINER_TYPE_MAP).map(([value, item]) => ({
|
||||
value: Number(value),
|
||||
label: item.text,
|
||||
})),
|
||||
},
|
||||
render: (_, record) => {
|
||||
const item = CONTAINER_TYPE_MAP[record.type ?? 0];
|
||||
return <Tag color={item?.color}>{item?.text ?? '-'}</Tag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '关联账单',
|
||||
dataIndex: 'bill_id',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) =>
|
||||
record.bill ? <Text copyable={{ text: record.bill.bill_no }}>{record.bill.bill_no}</Text> : '-',
|
||||
render: (_, record) => record.bill ? (
|
||||
<>
|
||||
<Text copyable={{ text: record.bill.bill_no }}>{record.bill.bill_no}</Text>
|
||||
<div className={'text-[12px] text-[#999]'}>{record.bill.bill_date}</div>
|
||||
</>
|
||||
) : '-',
|
||||
},
|
||||
{
|
||||
title: '周转筐数量',
|
||||
title: '压(回)筐数量',
|
||||
dataIndex: 'box_num',
|
||||
valueType: 'digit',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
initialValue: 0,
|
||||
rules: [{ required: true, message: '请输入周转筐数量' }],
|
||||
fieldProps: { min: 0, precision: 0 },
|
||||
align: 'center',
|
||||
render: (_, record) => <Text strong>{record.box_num}</Text>,
|
||||
render: (_, record) => <SignedNum value={record.box_num} />,
|
||||
},
|
||||
{
|
||||
title: '周转托盘数量',
|
||||
title: '压(回)托盘数量',
|
||||
dataIndex: 'tray_num',
|
||||
valueType: 'digit',
|
||||
hideInSearch: true,
|
||||
initialValue: 0,
|
||||
rules: [{ required: true, message: '请输入周转托盘数量' }],
|
||||
fieldProps: { min: 0, precision: 0 },
|
||||
align: 'center',
|
||||
render: (_, record) => <Text strong>{record.tray_num}</Text>,
|
||||
},
|
||||
{
|
||||
title: '当前待回(筐/托盘)',
|
||||
key: 'pending',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) =>
|
||||
record.store
|
||||
? `${record.store.pending_box_num ?? 0} / ${record.store.pending_tray_num ?? 0}`
|
||||
: '-',
|
||||
render: (_, record) => <SignedNum value={record.tray_num} />,
|
||||
},
|
||||
{
|
||||
title: '退回日期',
|
||||
dataIndex: 'return_date',
|
||||
valueType: 'dateRange',
|
||||
hideInTable: true,
|
||||
title: '筐单价',
|
||||
dataIndex: 'box_price',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) => `¥${Number(record.box_price ?? 0).toFixed(2)}`,
|
||||
},
|
||||
{
|
||||
title: '退回日期',
|
||||
dataIndex: 'return_date',
|
||||
valueType: 'date',
|
||||
title: '托盘单价',
|
||||
dataIndex: 'tray_price',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
initialValue: dayjs(),
|
||||
rules: [{ required: true, message: '请选择退回日期' }],
|
||||
align: 'center',
|
||||
render: (_, record) => `¥${Number(record.tray_price ?? 0).toFixed(2)}`,
|
||||
},
|
||||
{
|
||||
title: '抵扣(附加)金额',
|
||||
dataIndex: 'amount',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) => <SignedAmount value={record.amount} />,
|
||||
},
|
||||
{
|
||||
title: '操作人',
|
||||
@@ -119,20 +117,20 @@ const ContainerReturnPage: React.FC = () => {
|
||||
render: (_, record) => record.operator?.nickname ?? '-',
|
||||
},
|
||||
{
|
||||
title: '登记时间',
|
||||
title: '记录时间',
|
||||
dataIndex: 'created_at',
|
||||
valueType: 'dateRange',
|
||||
hideInForm: true,
|
||||
hideInTable: true,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '记录时间',
|
||||
dataIndex: 'created_at',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
valueType: 'textarea',
|
||||
hideInSearch: true,
|
||||
fieldProps: { rows: 2 },
|
||||
render: (v) => v || '-',
|
||||
},
|
||||
];
|
||||
|
||||
const tableProps: XinTableProps<IContainerReturn> = {
|
||||
@@ -140,25 +138,11 @@ const ContainerReturnPage: React.FC = () => {
|
||||
columns,
|
||||
rowKey: 'id',
|
||||
accessName: 'recon.containerReturn',
|
||||
// 压筐记录由账单生成,仅回筐记录可删除;均不允许编辑
|
||||
// 记录随账单生成,只读:不新增、不编辑、不删除
|
||||
addShow: false,
|
||||
editShow: false,
|
||||
deleteShow: (record) => record.type === 2,
|
||||
// 自定义提交:DatePicker 值为 dayjs 对象,格式化为 Y-m-d 后再提交
|
||||
handleFinish: async (values) => {
|
||||
await Create('/recon/container-return', {
|
||||
...values,
|
||||
return_date: dayjs(values.return_date).format('YYYY-MM-DD'),
|
||||
});
|
||||
message.success('回筐已登记');
|
||||
return true;
|
||||
},
|
||||
formProps: {
|
||||
grid: true,
|
||||
colProps: { span: 12 },
|
||||
rowProps: { gutter: 20 },
|
||||
layout: 'vertical',
|
||||
},
|
||||
modalProps: { width: 640, title: '回筐登记' },
|
||||
deleteShow: false,
|
||||
formProps: false,
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -166,7 +150,7 @@ const ContainerReturnPage: React.FC = () => {
|
||||
<div className="mb-5">
|
||||
<Title level={3}>回筐记录</Title>
|
||||
<Text type="secondary">
|
||||
生成账单时自动写入「压筐」记录并累加门店待回数量;门店退回周转筐/托盘时手动新增「回筐」登记并扣减待回,删除登记将恢复待回数量。
|
||||
周转筐/托盘跟账单走:生成账单时按填写数量自动写入;正数=压筐(附加金额),负数=回筐(抵扣金额),数量为 0 不生成记录。
|
||||
</Text>
|
||||
</div>
|
||||
<XinTable<IContainerReturn> {...tableProps} />
|
||||
|
||||
Reference in New Issue
Block a user