# coding=utf-8 from django.db.models import Sum,F from django.utils import timezone from apps.exceptions import CustomError from apps.goods.models import GoodsGodownEntry,GoodsGodownEntryDetail from apps.purchase.models import GodownEntry,GodownEntryDetail from apps.warehouse.models import WarehouseStock,WarehouseRecord,WarehouseStockRecord,WarehouseRecordDetail,WarehouseBackMap,Inventory,InventoryDetail from apps.product.models import ProductBase from apps.base import Formater class BizWarehouse(): # 更新冗余字段 @staticmethod def updateRedundant(warehouse_record): type = warehouse_record.type if type in (WarehouseRecord.CK_ZJ,WarehouseRecord.CK_XS,WarehouseRecord.CK_PK,WarehouseRecord.TL): BizWarehouse._updateDeliveredRedundant(warehouse_record) elif type == WarehouseRecord.TH: BizWarehouse._updateEntryBackRedundant(warehouse_record) @staticmethod def _updateDeliveredRedundant(warehouse_record): goods_entry_ids = [] inventory_ids = [] entry_ids = [] rows = WarehouseRecordDetail.objects.filter(warehouse_record=warehouse_record) for row in rows: stock_record = row.warehouse_stock_record deliver_count = BizWarehouse._getDeliveredCount(stock_record) deliver_amount = deliver_count * stock_record.entry_price goods_entry_detail = GoodsGodownEntryDetail.objects.filter(stock_record=stock_record).first() inventory_detail = InventoryDetail.objects.filter(warehouse_stock_record=stock_record).first() entry_detail = GodownEntryDetail.objects.filter(stock_record=stock_record).first() if goods_entry_detail: goods_entry_detail.deliver_count = deliver_count goods_entry_detail.deliver_amount = deliver_amount goods_entry_detail.save() goods_entry_ids.append(goods_entry_detail.main_id) if inventory_detail: inventory_detail.deliver_count = deliver_count inventory_detail.deliver_amount = deliver_amount inventory_detail.save() inventory_ids.append(inventory_detail.main_id) if entry_detail: entry_detail.deliver_count = deliver_count entry_detail.deliver_amount = deliver_amount entry_detail.save() entry_ids.append(entry_detail.main_id) BizWarehouse._updateMainRedundant(goods_entry_ids,inventory_ids,entry_ids) @staticmethod def _updateEntryBackRedundant(warehouse_record): goods_entry_ids = [] inventory_ids = [] entry_ids = [] rows = WarehouseRecordDetail.objects.filter(warehouse_record=warehouse_record) for row in rows: stock_record = row.warehouse_stock_record return_count = BizWarehouse._getEntryBackCount(stock_record) return_amount = return_count * stock_record.entry_price inventory_detail = InventoryDetail.objects.filter(warehouse_stock_record=stock_record).first() entry_detail = GodownEntryDetail.objects.filter(stock_record=stock_record).first() if inventory_detail: inventory_detail.return_count = return_count inventory_detail.return_amount = return_amount inventory_detail.save() inventory_ids.append(inventory_detail.main_id) if entry_detail: entry_detail.return_count = return_count entry_detail.return_amount = return_amount entry_detail.save() entry_ids.append(entry_detail.main_id) BizWarehouse._updateMainRedundant(goods_entry_ids, inventory_ids, entry_ids) @staticmethod def _getDeliveredCount(stock_record): count = 0 deliver_types = (WarehouseRecord.CK_ZJ,WarehouseRecord.CK_XS,WarehouseRecord.CK_PK,WarehouseRecord.TL) sum_row = WarehouseRecordDetail.objects.filter( warehouse_stock_record=stock_record, warehouse_record__type__in=deliver_types ).aggregate(total_count=Sum('count')) if sum_row: count = sum_row['total_count'] or 0 return -count @staticmethod def _getEntryBackCount(stock_record): count = 0 sum_row = WarehouseRecordDetail.objects.filter( warehouse_stock_record=stock_record, warehouse_record__type=WarehouseRecord.TH ).aggregate(total_count=Sum('count')) if sum_row: count = sum_row['total_count'] or 0 return -count @staticmethod def _updateMainRedundant(goods_entry_ids,inventory_ids,entry_ids): goods_entry_ids = list(set(goods_entry_ids)) inventory_ids = list(set(inventory_ids)) entry_ids = list(set(entry_ids)) for id in goods_entry_ids: row = GoodsGodownEntry.objects.filter(id=id).first() if row: row.update_redundant() for id in inventory_ids: row = Inventory.objects.filter(id=id).first() if row: row.update_redundant() for id in entry_ids: row = GodownEntry.objects.filter(id=id).first() if row: row.update_redundant() # 获取金额 @staticmethod def tryDelivered(product,warehouse,count): amount = 0 amount2 = 0 max_entry_price = 0 max_entry_price2 = 0 stock_records = WarehouseStockRecord.objects.filter( product=product, warehouse=warehouse, surplus_count__gt=0 ).order_by('entry_time') for row in stock_records: if row.surplus_count >= count: cur_count = count else: cur_count = row.surplus_count amount += cur_count * row.entry_price amount2 += cur_count * row.entry_price2 if row.entry_price > max_entry_price: max_entry_price = row.entry_price max_entry_price2 = row.entry_price2 count -= cur_count if not count: break if count: warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product) amount += count * warehouse_stock.avg_cost_price amount2 += count * warehouse_stock.avg_cost_price2 if warehouse_stock.avg_cost_price > max_entry_price: max_entry_price = warehouse_stock.avg_cost_price max_entry_price2 = warehouse_stock.avg_cost_price2 return amount, amount2, max_entry_price, max_entry_price2 # 入库 @staticmethod def entry(type,product,warehouse,supplier,entry_count,entry_price,entry_price2): rk_types = (WarehouseRecord.RK_ZJ,WarehouseRecord.RK_CG,WarehouseRecord.RK_PY) if entry_count <= 0: raise CustomError(u'[%s]入库数量不能小于等于0' % product.name) if entry_price < 0 or entry_price2 < 0: raise CustomError(u'[%s]入库价不能小于0' % product.name) if type not in rk_types: raise CustomError(u'[%s]入库类型错误' % product.name) warehouse_record = WarehouseRecord.objects.create( type=type, product=product, warehouse=warehouse, count=entry_count, amount=entry_count * entry_price, amount2=entry_count * entry_price2 ) warehouse_stock_record = WarehouseStockRecord.objects.create( product=product, warehouse=warehouse, supplier=supplier, entry_count=entry_count, entry_price=entry_price, entry_price2=entry_price2, surplus_count=entry_count ) WarehouseRecordDetail.objects.create( warehouse_record=warehouse_record, warehouse_stock_record=warehouse_stock_record, count=entry_count ) warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product) if type != WarehouseRecord.RK_PY: warehouse_stock.last_entry_price = entry_price product.last_entry_price = entry_price warehouse_stock.last_entry_time = timezone.now() product.last_entry_time = timezone.now() BizWarehouse._updateStock(warehouse_stock, warehouse_record) BizWarehouse._updateProductStock(product) return warehouse_stock_record # 出库 @staticmethod def delivered(type,product,warehouse,count): ck_types = (WarehouseRecord.CK_ZJ,WarehouseRecord.CK_XS,WarehouseRecord.CK_PK) if count <= 0: raise CustomError(u'[%s]出库数量不能小于等于0' % product.name) if type not in ck_types: raise CustomError(u'[%s]出库类型错误' % product.name) stock_records = WarehouseStockRecord.objects.filter( product=product, warehouse=warehouse, surplus_count__gt=0 ).order_by('entry_time') warehouse_record = WarehouseRecord.objects.create( type=type, product=product, warehouse=warehouse, count=-count, amount=0, amount2=0 ) BizWarehouse._deliveredForStockRecords(warehouse_record,stock_records) warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product) warehouse_stock.last_deliverd_time = timezone.now() product.last_deliverd_time = timezone.now() BizWarehouse._updateStock(warehouse_stock,warehouse_record) BizWarehouse._updateProductStock(product) BizWarehouse.updateRedundant(warehouse_record) return warehouse_record # 根据指定库存出库 @staticmethod def deliveredByStockRecord(type,stock_record,count): product = stock_record.product warehouse = stock_record.warehouse if count <= 0: raise CustomError(u'[%s]出库数量不能小于等于0' % product.name) if count > stock_record.surplus_count: raise CustomError(u'[%s]出库数量超出对应入库记录的剩余数量' % product.name) warehouse_record = WarehouseRecord.objects.create( type=type, product=product, warehouse=warehouse, count=-count, amount=0, amount2=0 ) stock_records = (stock_record,) BizWarehouse._deliveredForStockRecords(warehouse_record, stock_records) warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product) warehouse_stock.last_deliverd_time = timezone.now() product.last_deliverd_time = timezone.now() BizWarehouse._updateStock(warehouse_stock, warehouse_record) BizWarehouse._updateProductStock(product) BizWarehouse.updateRedundant(warehouse_record) return warehouse_record # 退料 @staticmethod def deliveredBack(warehouse_record, count): if count <= 0: raise CustomError(u'[%s]退料数量不能小于等于0' % warehouse_record.product.name) data = BizWarehouse._getDeliveredBackDetail(warehouse_record, count) res = BizWarehouse._deliveredBackByDetails(WarehouseRecord.TL,warehouse_record,data) BizWarehouse.updateRedundant(res) return res # 退货 @staticmethod def entryBack(stock_record,count): product = stock_record.product warehouse = stock_record.warehouse if count <= 0: raise CustomError(u'[%s]退货数量不能小于等于0' % product.name) if count > stock_record.surplus_count: raise CustomError(u'[%s]退货数量超出对应入库记录的剩余数量' % product.name) warehouse_record = WarehouseRecord.objects.create( type=WarehouseRecord.TH, product=product, warehouse=warehouse, count=-count, amount=0, amount2=0 ) stock_records = (stock_record,) BizWarehouse._deliveredForStockRecords(warehouse_record, stock_records) warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product) BizWarehouse._updateStock(warehouse_stock, warehouse_record) BizWarehouse._updateProductStock(product) BizWarehouse.updateRedundant(warehouse_record) return warehouse_record # 批量退货 @staticmethod def entryBatchBack(product,warehouse,count,supplier=None): if count <= 0: raise CustomError(u'[%s]退货数量不能小于等于0' % product.name) stock_records = WarehouseStockRecord.objects.filter( product=product, warehouse=warehouse, surplus_count__gt=0 ) if supplier: stock_records = stock_records.filter( supplier=supplier ) stock_records = stock_records.order_by('entry_time') warehouse_record = WarehouseRecord.objects.create( type=WarehouseRecord.TH, product=product, warehouse=warehouse, count=-count, amount=0, amount2=0 ) BizWarehouse._deliveredForStockRecords(warehouse_record, stock_records) warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product) BizWarehouse._updateStock(warehouse_stock, warehouse_record) BizWarehouse._updateProductStock(product) BizWarehouse.updateRedundant(warehouse_record) return warehouse_record @staticmethod def _getDeliveredBackDetail(warehouse_record, count): details = WarehouseRecordDetail.objects.filter( warehouse_record=warehouse_record ).order_by('-warehouse_stock_record__entry_time') data = [] for detail in details: back_count = (WarehouseBackMap.objects.filter(delivered_detail=detail).aggregate(count_sum=Sum('back_count')))['count_sum'] or 0 if back_count >= -detail.count: continue temp = -(detail.count + back_count) if temp >= count: cur_count = count else: cur_count = temp data.append({'delivered_detail': detail, 'back_count': cur_count}) count -= cur_count if not count: break if count: raise CustomError(u'[%s]退料数量超出出库剩余数量' % warehouse_record.product.name) return data @staticmethod def _deliveredBackByDetails(type, warehouse_record, details): product = warehouse_record.product warehouse = warehouse_record.warehouse item = WarehouseRecord.objects.create( type=type, product=warehouse_record.product, warehouse=warehouse_record.warehouse, count=0, amount=0, amount2=0 ) for detail in details: warehouse_stock_record = detail['delivered_detail'].warehouse_stock_record back_detail = WarehouseRecordDetail.objects.create( warehouse_record=item, warehouse_stock_record=warehouse_stock_record, count=detail['back_count'] ) warehouse_stock_record.surplus_count += detail['back_count'] warehouse_stock_record.save() WarehouseBackMap.objects.create( back_detail=back_detail, delivered_detail=detail['delivered_detail'], back_count=detail['back_count'] ) item.count += detail['back_count'] sum_row = WarehouseRecordDetail.objects.filter( warehouse_record=item ).aggregate( sum_amount=Sum(F('count') * F('warehouse_stock_record__entry_price')), sum_amount2=Sum(F('count') * F('warehouse_stock_record__entry_price2')) ) item.amount = sum_row['sum_amount'] item.amount2 = sum_row['sum_amount2'] item.save() warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product) BizWarehouse._updateStock(warehouse_stock, item) BizWarehouse._updateProductStock(product) return item @staticmethod def _deliveredForStockRecords(warehouse_record,stock_records): product = warehouse_record.product temp_count = -warehouse_record.count for row in stock_records: if row.surplus_count >= temp_count: cur_count = temp_count else: cur_count = row.surplus_count row.surplus_count -= cur_count row.save() WarehouseRecordDetail.objects.create( warehouse_record=warehouse_record, warehouse_stock_record=row, count=-cur_count ) temp_count -= cur_count if not temp_count: break if temp_count: raise CustomError(u'[%s]库存不足' % product.name) sum_row = WarehouseRecordDetail.objects.filter( warehouse_record=warehouse_record ).aggregate( sum_amount=Sum(F('count') * F('warehouse_stock_record__entry_price')), sum_amount2=Sum(F('count') * F('warehouse_stock_record__entry_price2')) ) warehouse_record.amount = sum_row['sum_amount'] warehouse_record.amount2 = sum_row['sum_amount2'] warehouse_record.save() @staticmethod def _updateStock(warehouse_stock,warehouse_record): warehouse_stock.count += warehouse_record.count warehouse_stock.amount += warehouse_record.amount warehouse_stock.amount2 += warehouse_record.amount2 if warehouse_stock.count != 0: warehouse_stock.avg_cost_price = warehouse_stock.amount / warehouse_stock.count warehouse_stock.avg_cost_price2 = warehouse_stock.amount2 / warehouse_stock.count warehouse_record.cur_count = warehouse_stock.count warehouse_record.cur_amount = warehouse_stock.amount warehouse_record.cur_amount2 = warehouse_stock.amount2 warehouse_record.save() warehouse_stock.save() @staticmethod def _updateProductStock(product): sum_row = WarehouseStock.objects.filter( product=product ).aggregate( count=Sum('count'), amount=Sum('amount'), amount2=Sum('amount2') ) product.stock_count = sum_row['count'] product.stock_amount = sum_row['amount'] product.stock_amount2 = sum_row['amount2'] if product.stock_count != 0: product.avg_cost_price = product.stock_amount / product.stock_count product.avg_cost_price2 = product.stock_amount2 / product.stock_count product.save() class GetWarehouseSrockRecord(): @staticmethod def getRecord(product, warehouse, supplier=None): record_data = [] record_rows = WarehouseStockRecord.objects.filter(warehouse_id=warehouse, product_id=product, surplus_count__gt=0) if supplier: record_rows = record_rows.filter(supplier_id=supplier) for record_row in record_rows: product_base = record_row.product if product_base.type == ProductBase.GOODS: g_row = GoodsGodownEntryDetail.objects.filter(stock_record_id=record_row.id).first() else: g_row = GodownEntryDetail.objects.filter(stock_record_id=record_row.id).first() if not g_row: g_row = InventoryDetail.objects.filter(warehouse_stock_record_id=record_row.id).first() record_item = { 'stock_id': record_row.id, 'no': g_row.main.no, 'entry_price': Formater.formatPriceShow(record_row.entry_price), 'surplus_count': Formater.formatCountShow(record_row.surplus_count), 'detail_id': g_row.id, 'notes': g_row.main.notes or '' } record_data.append(record_item) return record_data