admin/addon/stock/model/stock/Document.php

663 lines
25 KiB
PHP

<?php
/**
* ThinkShop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 上海牛之云网络科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.cdcloudshop.com
* =========================================================
*/
namespace addon\stock\model\stock;
use app\model\goods\Goods;
use app\model\stock\GoodsStock;
use app\model\store\Store;
use app\model\BaseModel;
use app\model\storegoods\StoreGoods;
/**
* 单据管理
* @author Administrator
*
*/
class Document extends BaseModel
{
//单据审核状态
const DOCUMENT_DRAFT = 0;
//单据审核状态
const DOCUMENT_AUDIT = 1;
//单据审核状态
const DOCUMENT_AUDITED = 2;
public $document_status = [
self::DOCUMENT_DRAFT => [
'status' => self::DOCUMENT_DRAFT,
'name' => '草稿'
],
self::DOCUMENT_AUDIT => [
'status' => self::DOCUMENT_AUDIT,
'name' => '待审核'
],
self::DOCUMENT_AUDITED => [
'status' => self::DOCUMENT_AUDITED,
'name' => '已审核'
]
];
/**
* 添加采购入库
* @param unknown $site_id
* @param unknown $user_info
* @param unknown $goods_sku_list
* @param unknown $document_info
* @return multitype:
*/
public function addPurchase($params)
{
$params['document_type'] = 'PURCHASE';
$res = $this->addDocument($params);
//此处要针对商品库存以及单价处理
return $res;
}
/**
* 其他入库
* @param int $site_id
* @param array $user_info
* @param string $goods_sku_list sku_id:price:num,sku_id:price:num
* @param array $document_info
*/
public function addOtherInput($params)
{
//商品库存改变进行处理
$params['document_type'] = 'OTHERRK';
$res = $this->addDocument($params);
return $res;
}
/**
* 退货入库
* @param int $site_id
* @param array $user_info
* @param string $goods_sku_list sku_id:price:num,sku_id:price:num
* @param array $document_info
*/
public function addRefundInput($params)
{
//商品库存改变进行处理
$params['document_type'] = 'REFUND';
$res = $this->addDocument($params);
return $res;
}
/**
* 修改采购单(只针对草稿)
* @param int $document_id
* @param array $user_info
* @param string $goods_sku_list
* @param array $document_info
* @return multitype:string mixed
*/
public function editPurchase($document_id, $user_info, $goods_sku_list, $document_info)
{
$goods_sku_list_array = $this->getSkuListArray($goods_sku_list);
$res = $this->editDocument($document_id, $user_info, $goods_sku_list_array, $document_info);
return $res;
}
/**
* 销售出库
* @param int $site_id
* @param array $user_info
* @param string $goods_sku_list sku_id:price:num,sku_id:price:num
* @param array $document_info
*/
public function addSell($params)
{
//商品库存改变之后要针对性修改
$params['document_type'] = 'SEAILCK';
$res = $this->addDocument($params);
return $res;
}
/**
* 其他出库
* @param int $site_id
* @param array $user_info
* @param string $goods_sku_list sku_id:price:num,sku_id:price:num
* @param array $document_info
*/
public function addOtherOutput($params)
{
//商品库存改变之后要针对性修改
$params['document_type'] = 'OTHERCK';
$res = $this->addDocument($params);
return $res;
}
/**
* 添加单据
* @param int $site_id
* @param string $document_type
* @param array $user_info
* @param string $goods_list
* @param array $document_info
*/
public function addDocument($params)
{
$site_id = $params['site_id'];
$document_type = $params['document_type'];
$user_info = $params['user_info'];
$goods_sku_list = $params['goods_sku_list'];
$remark = $params['remark'] ?? '';
$store_id = $params['store_id'];
//结构化
$goods_sku_list = $this->getSkuListArray($goods_sku_list, $store_id);
$code = $goods_sku_list['code'] ?? 0;
if($code < 0){
return $goods_sku_list;
}
$is_out_stock = $params['is_out_stock'] ?? 0;//当前单据是否变动销售库存
$promotion_money = $params['promotion_money'] ?? 0;
$time = $params['time'] ?? 0;
$relate_id = $params['relate_id'] ?? 0;
$relate_type = $params['relate_type'] ?? 0;
model('stock_document')->startTrans();
try {
$document_type_info = $this->getDocumentTypeInfo(['key' => $document_type]);
$prefix = $document_type_info[ 'prefix' ];//单据前缀
$type = $document_type_info[ 'type' ];//出入库类型
$document_no = $this->createDocumentNo($prefix);
//查询门店名称信息
$store_name = (new Store())->getStoreName([[ 'store_id', '=', $store_id ]])[ 'data' ] ?? '';
if (empty($store_name)) {
model('stock_document')->rollback();
return $this->success([], '找不到所选的门店');
}
//计算单据商品总额(累加)
// if ($type == 'input') {
$goods_money = getArraySum($goods_sku_list, 'goods_price', 'goods_num');
// }else{
// $goods_money = 0;
// }
$common_data = array (
'operater' => $user_info[ 'uid' ] ?? 0,
'operater_name' => $user_info[ 'username' ] ?? '系统',
'create_time' => time(),
);
$document_money = $goods_money - $promotion_money;
$data = array(
'site_id' => $site_id,
'key' => $document_type,
'type' => $type,
'document_no' => $document_no,//单据单号
'goods_money' => $goods_money,
'store_id' => $store_id,
'store_name' => $store_name,
'is_out_stock' => $is_out_stock,
'allot_id' => $params['allot_id'] ?? 0,
'inventory_id' => $params['inventory_id'] ?? 0,
'document_money' => $document_money,
'remark' => $remark,
'time' => $time,
'relate_id' => $relate_id,
'relate_type' => $relate_type
);
$data = array_merge($data, $common_data);
$document_id = model('stock_document')->add($data);
$goods_model = new Goods();
foreach ($goods_sku_list as $k => $v) {
$item_data = $v;
$item_data = array_merge($item_data, $common_data);
$item_data[ 'document_id' ] = $document_id;
$item_data[ 'site_id' ] = $site_id;
$item_data[ 'store_id' ] = $store_id;
// $sku_info = $goods_model->getGoodsSkuInfo([ [ 'sku_id', '=', $v[ 'goods_sku_id' ] ] ])[ 'data' ] ?? [];
// $store_goods_model = new StoreGoods();
// $store_sku_info = $store_goods_model->getStoreGoodsSkuInfo([ [ 'sku_id', '=', $v[ 'goods_sku_id' ] ], ['store_id', '=', $store_id] ])[ 'data' ] ?? [];
// if ($type == 'output') {
// $item_data[ 'goods_price' ] = $store_sku_info[ 'cost_price' ];
// }
// $item_data[ 'goods_sku_name' ] = $v[ 'sku_name' ];
model('stock_document_goods')->add($item_data);
}
$result = $this->audit([ 'document_id' => $document_id, 'site_id' => $site_id ]);
if ($result[ 'code' ] < 0) {
model('stock_document')->rollback();
return $result;
}
model('stock_document')->commit();
return $this->success($document_id);
} catch (\Exception $e) {
model('stock_document')->rollback();
return $this->error($e->getMessage().$e->getFile().$e->getLine());
}
}
/**
* 创建出入库单号
* @param $prefix
* @return string
*/
public function createDocumentNo($prefix){
$document_no = $prefix . date('ymdhis', time()) . rand(1111, 9999);
return $document_no;
}
/**
* 修改单据
* @param int $document_id
* @param array $user_info
* @param string $goods_list
* @param array $document_info
*/
private function editDocument($document_id, $user_info, $goods_sku_list_array, $document_info)
{
model('stock_document')->startTrans();
try {
$document_info[ 'goods_money' ] = 0;
foreach ($goods_sku_list_array as $k => $v) {
$document_info[ 'goods_money' ] += $v[ 'goods_price' ] * $v[ 'goods_num' ];
}
$document_info[ 'document_money' ] = $document_info[ 'goods_money' ] - $document_info[ 'promotion_money' ];
$data = array_merge($document_info, [ 'uid' => $user_info[ 'sys_uid' ], 'nickname' => $user_info[ 'nickname' ] ]);
$res = model('stock_document')->update($data, [ 'document_id' => $document_id ]);
model('stock_document_goods')->delete([ 'document_id' => $document_id ]);
foreach ($goods_sku_list_array as $k => $v) {
$v[ 'document_id' ] = $document_id;
model('stock_document_goods')->add($v);
}
model('stock_document')->commit();
return $this->success($res);
} catch (\Exception $e) {
model('stock_document')->rollback();
return $this->error($e->getMessage());
}
}
/**
* 确认单据单据(转为待审核)
* @param int $document_id
* @param array $user_info
*/
public function confirmDocument($document_id, $user_info)
{
}
/**
* 审核单据
* @param int $document_id
* @param array $user_info
*/
private function verifyDocument($document_id, $user_info)
{
}
/**
* 获取商品列表
* @param unknown $goods_sku_list sku_id:price:num,sku_id:price:num
*/
private function getSkuListArray($goods_sku_list, $store_id)
{
$store_goods_model = new StoreGoods();
$goods_sku_list_array = [];
foreach ($goods_sku_list as $k => $goods_sku) {
$goods_sku_id = $goods_sku[ 'goods_sku_id' ] ?? 0;
if ($goods_sku_id <= 0) {
return $this->error([]);
}
$goods_sku_info = model('goods_sku')->getInfo([
'sku_id' => $goods_sku_id
]);
$goods_num = $goods_sku[ 'goods_num' ] ?? 0;
if ($goods_num <= 0) {
return $this->error([], '商品:' . $goods_sku_info[ 'sku_name' ] . '入库数量不能为空!');
}
if(!isset($goods_sku['goods_price'])){
$store_sku_info = $store_goods_model->getStoreGoodsSkuInfo([ [ 'sku_id', '=', $goods_sku_id ], ['store_id', '=', $store_id] ])[ 'data' ] ?? [];
$goods_price = $store_sku_info['cost_price'];
}else{
$goods_price = $goods_sku['goods_price'];
}
$goods_sku_list_array[] = [
'goods_id' => $goods_sku_info[ 'goods_id' ],
'goods_sku_id' => $goods_sku_info[ 'sku_id' ],
'goods_sku_name' => $goods_sku_info[ 'sku_name' ],
'goods_sku_no' => $goods_sku_info[ 'sku_no' ],
'goods_sku_img' => $goods_sku_info[ 'sku_image' ],
'goods_sku_spec' => $goods_sku_info[ 'spec_name' ],
'goods_unit' => $goods_sku_info[ 'unit' ] ?? '件',
'goods_num' => $goods_num,
'goods_price' => $goods_price,
'goods_remark' => ''
];
}
return $goods_sku_list_array;
}
/**
* 获取单据分页列表
* @param array $condition
* @param int $page
* @param string $page_size
* @param string $order
* @param string $field
* @return multitype:string mixed
*/
public function getDocumentPageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = '', $field = '*')
{
$document_list = model('stock_document')->pageList($condition, $field, $order, $page, $page_size);
if (!empty($document_list[ 'list' ])) {
foreach ($document_list[ 'list' ] as $k => $v) {
//单据产品项
// $document_goods_list = model('stock_document_goods')->getList([
// 'document_id' => $v['document_id']
// ]);
// $document_list['list'][$k]['goods_sku_list_array'] = $document_goods_list;
$type_info = $this->getDocumentTypeInfo([ 'key' => $v[ 'key' ] ]);
$type_name = '';
if (!empty($type_info)) {
$type_name = $type_info[ 'name' ];
}
$document_list[ 'list' ][ $k ][ 'type_name' ] = $type_name;
$document_list[ 'list' ][ $k ][ 'status_name' ] = $this->document_status[ $v[ 'status' ] ][ 'name' ];
}
}
return $this->success($document_list);
}
/**
* 审核
* @param $params
*/
public function audit($params){
$document_id = $params[ 'document_id' ] ?? 0;
$site_id = $params[ 'site_id' ];
$condition = array (
[ 'document_id', '=', $document_id ],
[ 'site_id', '=', $site_id ],
// ['status', '=', 1]
);
$document_info = model('stock_document')->getInfo($condition);
if (empty($document_info)) {
return $this->error([], '单据不存在');
}
$result = $this->complete($params);
if($result['code'] < 0)
return $result;
$data = array (
'status' => 2,
'verify_time' => time(),
);
model('stock_document')->update($data, $condition);
return $this->success();
}
/**
* 单据完成(通过审核...)
* @param $params
*/
public function complete($params)
{
//计算统计改变商品的成本价和库存
$document_id = $params[ 'document_id' ] ?? 0;
$site_id = $params[ 'site_id' ];
$condition = array (
[ 'document_id', '=', $document_id ],
[ 'site_id', '=', $site_id ]
);
$document_info = model('stock_document')->getInfo($condition);
if (empty($document_info)) {
return $this->error([], '单据不存在');
}
//影响库存和成本均价
$result = $this->changeGoods($document_info);
if (!empty($result[ 'code' ]) && $result[ 'code' ] < 0)
return $result;
return $this->success();
}
/**
* 单据详情
* @param array $condition
* @param string $field
*/
public function getDocumentInfo($condition = [], $field = '*')
{
$document_info = model('stock_document')->getInfo($condition, $field);
if (!empty($document_info)) {
//单据产品项
$document_goods_list = model('stock_document_goods')->getList([
'document_id' => $document_info[ 'document_id' ]
]);
$document_info[ 'goods_sku_list_array' ] = $document_goods_list;
$document_info[ 'goods_piece' ] = 0;
$document_info[ 'goods_total_price' ] = 0.00;
$goods_model = new Goods();
foreach ($document_goods_list as $key => $value) {
$document_info[ 'goods_sku_list_array' ][ $key ][ 'goods_sum' ] = floatval($value[ 'goods_num' ] * $value[ 'goods_price' ]);
$document_info[ 'goods_piece' ] += intval($value[ 'goods_num' ]);
$document_info[ 'goods_total_price' ] += $document_info[ 'goods_sku_list_array' ][ $key ][ 'goods_sum' ];
}
$type_info = $this->getDocumentTypeInfo([ 'key' => $document_info[ 'key' ] ]);
$type_name = '';
if (!empty($type_info)) {
$type_name = $type_info[ 'name' ];
}
$document_info[ 'type_name' ] = $type_name;
$document_info[ 'status_data' ] = $this->document_status[ $document_info[ 'status' ] ];
$document_info[ 'goods_count' ] = count($document_info[ 'goods_sku_list_array' ]);
$document_info[ 'goods_total_price' ] = floatval($document_info[ 'goods_total_price' ]);
$document_info[ 'create_time' ] = date('Y-m-d H:i:s', $document_info[ 'create_time' ]);
}
return $this->success($document_info);
}
/********************************************************************* 类型调用 ********************************************************/
/**
* 单据类型
* @param array $condition
* @param string $field
* @param string $order
* @param null $limit
* @return array
*/
public function getDocumentTypeList()
{
return $this->success((new Stock())->document_type_list);
}
/**
* 多场景调用单据类型
* @param $params
* @return array
*/
public function getDocumentType($params)
{
$list = (new Stock())->document_type_list;
return $list;
}
/**
* 单据类型信息
* @param $params
* @return array
*/
public function getDocumentTypeInfo($params)
{
$key = $params[ 'key' ];
$type_list = $this->getDocumentType([]);
return $type_list[ $key ] ?? [];
}
/********************************************************************* 类型调用 ********************************************************/
/**
* 获取单据项分页列表
* @param array $condition
* @param int $page
* @param int $page_size
* @param string $order
* @param string $field
* @return array
*/
public function getDocumentGoodsPageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = '', $field = '*')
{
$join = [
[ 'goods_sku gs', 'gs.sku_id = dg.goods_sku_id', 'left' ],
[ 'stock_document d', 'd.document_id = dg.document_id', 'inner' ],
// [ 'stock_document_type dt', 'dt.key = d.key', 'inner' ]
];
$document_list = model('stock_document_goods')->pageList($condition, $field, $order, $page, $page_size, 'dg', $join);
if (!empty($document_list[ 'list' ])) {
foreach ($document_list[ 'list' ] as $k => &$v) {
$v[ 'status_name' ] = $this->document_status[ $v[ 'status' ] ][ 'name' ];
$v['name'] = $this->getDocumentTypeInfo(['key' => $v['key']])['name'] ?? '';
}
}
return $this->success($document_list);
}
/**
* 获取单据项数量
* @param array $condition
* @return array
*/
public function getDocumentGoodsCount($condition = [])
{
$res = model('stock_document_goods')->getCount($condition);
return $this->success($res);
}
/**
* 采购入库改变商品库存与成本价
* @param $goods_sku_list_array
* @param $document_info
* @return array
*/
public function changeGoods($document_info)
{
$document_id = $document_info['document_id'] ?? 0;
$condition = array(
['document_id', '=', $document_id]
);
$goods_sku_list_array = model('stock_document_goods')->getList($condition);
$store_goods_model = new StoreGoods();
if (!empty($goods_sku_list_array)) {
$goods_stock_model = new GoodsStock();
$type = $document_info[ 'type' ];
$store_id = $document_info[ 'store_id' ];
$site_id = $document_info[ 'site_id' ];
$is_out_stock = $document_info['is_out_stock'] ?? 0;
foreach ($goods_sku_list_array as $goods_sku_item) {
$item_sku_id = $goods_sku_item[ 'goods_sku_id' ];
$item_goods_id = $goods_sku_item[ 'goods_id' ];
$item_goods_price = $goods_sku_item[ 'goods_price' ];
$change_num = $goods_sku_item[ 'goods_num' ];
if ($type == 'output') {
$change_num = -$change_num;
}
$sku_condition = array (
[ 'sku_id', '=', $item_sku_id ]
);
$goods_sku_info = model('goods_sku')->getInfo($sku_condition);
$goods_sku_stock = $goods_sku_info[ 'real_stock' ];
$goods_sku_cost_price = $goods_sku_info[ 'cost_price' ];
$after_stock = $goods_sku_stock + $change_num;
//todo 不同的业务模式,待商榷
// if ($after_stock < 0) {
// return $this->error([], '库存不能小于0');
// }
$store_goods_sku_condition = array (
[ 'store_id', '=', $store_id ],
[ 'goods_id', '=', $item_goods_id ],
[ 'sku_id', '=', $item_sku_id ]
);
$store_goods_sku_info = $store_goods_model->getStoreGoodsSkuInfo($store_goods_sku_condition)['data'] ?? [];
$store_goods_sku_cost_price = $store_goods_sku_info['cost_price'] ?? 0;
$store_goods_sku_stock = $store_goods_sku_info[ 'real_stock' ] ?? 0;
// $store_goods_sku_sale_stock = $store_goods_sku_info[ 'sale_stock' ] ?? 0;//销售库存
$after_store_stock = $store_goods_sku_stock + $change_num;
//门店sku库存
$total_stock = $goods_sku_stock + $change_num;
$total_goods_money = $item_goods_price * $change_num;
//计算门店 成本均价
// 判断采购成本与之前的成本是否不同
if ($store_goods_sku_cost_price != $item_goods_price) {
//只有库存增加才会重新计算成本价(只有入库)
if ($change_num > 0) {
$total_cost_price = $store_goods_sku_stock * $store_goods_sku_cost_price + $change_num * $item_goods_price;
if ($total_cost_price >= 0 && $total_stock > 0) {
$curr_cost_price = round(( $total_cost_price / $total_stock ), 2);
model('store_goods_sku')->update([ 'cost_price' => $curr_cost_price ], $store_goods_sku_condition);
$total_goods_cost_price = $goods_sku_stock*$goods_sku_cost_price + $change_num * $item_goods_price;
$after_goods_cost_price = round($total_goods_cost_price/$after_stock, 2);
model('goods_sku')->update([ 'cost_price' => $after_goods_cost_price ], $sku_condition);
}
}
}
$stock_params = array(
'store_id' => $store_id,
'stock' => abs($change_num),
'goods_id' => $item_goods_id,
'sku_id' => $item_sku_id
);
//调用公共的库存函数库
if($change_num > 0){
$goods_stock_model->incGoodsStock($stock_params);
}else if($change_num < 0){
//防重复扣销售库存
$stock_params['is_out_stock'] = $is_out_stock;
$result = $goods_stock_model->decGoodsStock($stock_params);
if($result['code'] < 0)
return $result;
}
$item_data = array (
'before_stock' => $goods_sku_stock,
'after_stock' => $after_stock,
'before_goods_price' => $goods_sku_cost_price,
'after_goods_price' => $after_goods_cost_price ?? $goods_sku_cost_price,
'before_store_stock' => $store_goods_sku_stock,
'before_store_goods_price' => $store_goods_sku_cost_price,
'after_store_stock' => $after_store_stock,
'after_store_goods_price' => $curr_cost_price ?? $store_goods_sku_cost_price,
'total_goods_money' => $total_goods_money
);
$item_condition = array (
[ 'document_goods_id', '=', $goods_sku_item[ 'document_goods_id' ] ]
);
model('stock_document_goods')->update($item_data, $item_condition);
}
}
return $this->success();
}
}