biz.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. # coding=utf-8
  2. from django.db.models import Sum,F
  3. from django.utils import timezone
  4. from apps.exceptions import CustomError
  5. from apps.goods.models import GoodsGodownEntry,GoodsGodownEntryDetail
  6. from apps.purchase.models import GodownEntry,GodownEntryDetail
  7. from apps.warehouse.models import WarehouseStock,WarehouseRecord,WarehouseStockRecord,WarehouseRecordDetail,WarehouseBackMap,Inventory,InventoryDetail
  8. from apps.product.models import ProductBase
  9. from apps.base import Formater
  10. class BizWarehouse():
  11. # 更新冗余字段
  12. @staticmethod
  13. def updateRedundant(warehouse_record):
  14. type = warehouse_record.type
  15. if type in (WarehouseRecord.CK_ZJ,WarehouseRecord.CK_XS,WarehouseRecord.CK_PK,WarehouseRecord.TL):
  16. BizWarehouse._updateDeliveredRedundant(warehouse_record)
  17. elif type == WarehouseRecord.TH:
  18. BizWarehouse._updateEntryBackRedundant(warehouse_record)
  19. @staticmethod
  20. def _updateDeliveredRedundant(warehouse_record):
  21. goods_entry_ids = []
  22. inventory_ids = []
  23. entry_ids = []
  24. rows = WarehouseRecordDetail.objects.filter(warehouse_record=warehouse_record)
  25. for row in rows:
  26. stock_record = row.warehouse_stock_record
  27. deliver_count = BizWarehouse._getDeliveredCount(stock_record)
  28. deliver_amount = deliver_count * stock_record.entry_price
  29. goods_entry_detail = GoodsGodownEntryDetail.objects.filter(stock_record=stock_record).first()
  30. inventory_detail = InventoryDetail.objects.filter(warehouse_stock_record=stock_record).first()
  31. entry_detail = GodownEntryDetail.objects.filter(stock_record=stock_record).first()
  32. if goods_entry_detail:
  33. goods_entry_detail.deliver_count = deliver_count
  34. goods_entry_detail.deliver_amount = deliver_amount
  35. goods_entry_detail.save()
  36. goods_entry_ids.append(goods_entry_detail.main_id)
  37. if inventory_detail:
  38. inventory_detail.deliver_count = deliver_count
  39. inventory_detail.deliver_amount = deliver_amount
  40. inventory_detail.save()
  41. inventory_ids.append(inventory_detail.main_id)
  42. if entry_detail:
  43. entry_detail.deliver_count = deliver_count
  44. entry_detail.deliver_amount = deliver_amount
  45. entry_detail.save()
  46. entry_ids.append(entry_detail.main_id)
  47. BizWarehouse._updateMainRedundant(goods_entry_ids,inventory_ids,entry_ids)
  48. @staticmethod
  49. def _updateEntryBackRedundant(warehouse_record):
  50. goods_entry_ids = []
  51. inventory_ids = []
  52. entry_ids = []
  53. rows = WarehouseRecordDetail.objects.filter(warehouse_record=warehouse_record)
  54. for row in rows:
  55. stock_record = row.warehouse_stock_record
  56. return_count = BizWarehouse._getEntryBackCount(stock_record)
  57. return_amount = return_count * stock_record.entry_price
  58. inventory_detail = InventoryDetail.objects.filter(warehouse_stock_record=stock_record).first()
  59. entry_detail = GodownEntryDetail.objects.filter(stock_record=stock_record).first()
  60. if inventory_detail:
  61. inventory_detail.return_count = return_count
  62. inventory_detail.return_amount = return_amount
  63. inventory_detail.save()
  64. inventory_ids.append(inventory_detail.main_id)
  65. if entry_detail:
  66. entry_detail.return_count = return_count
  67. entry_detail.return_amount = return_amount
  68. entry_detail.save()
  69. entry_ids.append(entry_detail.main_id)
  70. BizWarehouse._updateMainRedundant(goods_entry_ids, inventory_ids, entry_ids)
  71. @staticmethod
  72. def _getDeliveredCount(stock_record):
  73. count = 0
  74. deliver_types = (WarehouseRecord.CK_ZJ,WarehouseRecord.CK_XS,WarehouseRecord.CK_PK,WarehouseRecord.TL)
  75. sum_row = WarehouseRecordDetail.objects.filter(
  76. warehouse_stock_record=stock_record,
  77. warehouse_record__type__in=deliver_types
  78. ).aggregate(total_count=Sum('count'))
  79. if sum_row:
  80. count = sum_row['total_count'] or 0
  81. return -count
  82. @staticmethod
  83. def _getEntryBackCount(stock_record):
  84. count = 0
  85. sum_row = WarehouseRecordDetail.objects.filter(
  86. warehouse_stock_record=stock_record,
  87. warehouse_record__type=WarehouseRecord.TH
  88. ).aggregate(total_count=Sum('count'))
  89. if sum_row:
  90. count = sum_row['total_count'] or 0
  91. return -count
  92. @staticmethod
  93. def _updateMainRedundant(goods_entry_ids,inventory_ids,entry_ids):
  94. goods_entry_ids = list(set(goods_entry_ids))
  95. inventory_ids = list(set(inventory_ids))
  96. entry_ids = list(set(entry_ids))
  97. for id in goods_entry_ids:
  98. row = GoodsGodownEntry.objects.filter(id=id).first()
  99. if row:
  100. row.update_redundant()
  101. for id in inventory_ids:
  102. row = Inventory.objects.filter(id=id).first()
  103. if row:
  104. row.update_redundant()
  105. for id in entry_ids:
  106. row = GodownEntry.objects.filter(id=id).first()
  107. if row:
  108. row.update_redundant()
  109. # 获取金额
  110. @staticmethod
  111. def tryDelivered(product,warehouse,count):
  112. amount = 0
  113. amount2 = 0
  114. max_entry_price = 0
  115. max_entry_price2 = 0
  116. stock_records = WarehouseStockRecord.objects.filter(
  117. product=product,
  118. warehouse=warehouse,
  119. surplus_count__gt=0
  120. ).order_by('entry_time')
  121. for row in stock_records:
  122. if row.surplus_count >= count:
  123. cur_count = count
  124. else:
  125. cur_count = row.surplus_count
  126. amount += cur_count * row.entry_price
  127. amount2 += cur_count * row.entry_price2
  128. if row.entry_price > max_entry_price:
  129. max_entry_price = row.entry_price
  130. max_entry_price2 = row.entry_price2
  131. count -= cur_count
  132. if not count:
  133. break
  134. if count:
  135. warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
  136. amount += count * warehouse_stock.avg_cost_price
  137. amount2 += count * warehouse_stock.avg_cost_price2
  138. if warehouse_stock.avg_cost_price > max_entry_price:
  139. max_entry_price = warehouse_stock.avg_cost_price
  140. max_entry_price2 = warehouse_stock.avg_cost_price2
  141. return amount, amount2, max_entry_price, max_entry_price2
  142. # 入库
  143. @staticmethod
  144. def entry(type,product,warehouse,supplier,entry_count,entry_price,entry_price2):
  145. rk_types = (WarehouseRecord.RK_ZJ,WarehouseRecord.RK_CG,WarehouseRecord.RK_PY)
  146. if entry_count <= 0:
  147. raise CustomError(u'[%s]入库数量不能小于等于0' % product.name)
  148. if entry_price < 0 or entry_price2 < 0:
  149. raise CustomError(u'[%s]入库价不能小于0' % product.name)
  150. if type not in rk_types:
  151. raise CustomError(u'[%s]入库类型错误' % product.name)
  152. warehouse_record = WarehouseRecord.objects.create(
  153. type=type,
  154. product=product,
  155. warehouse=warehouse,
  156. count=entry_count,
  157. amount=entry_count * entry_price,
  158. amount2=entry_count * entry_price2
  159. )
  160. warehouse_stock_record = WarehouseStockRecord.objects.create(
  161. product=product,
  162. warehouse=warehouse,
  163. supplier=supplier,
  164. entry_count=entry_count,
  165. entry_price=entry_price,
  166. entry_price2=entry_price2,
  167. surplus_count=entry_count
  168. )
  169. WarehouseRecordDetail.objects.create(
  170. warehouse_record=warehouse_record,
  171. warehouse_stock_record=warehouse_stock_record,
  172. count=entry_count
  173. )
  174. warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
  175. if type != WarehouseRecord.RK_PY:
  176. warehouse_stock.last_entry_price = entry_price
  177. product.last_entry_price = entry_price
  178. warehouse_stock.last_entry_time = timezone.now()
  179. product.last_entry_time = timezone.now()
  180. BizWarehouse._updateStock(warehouse_stock, warehouse_record)
  181. BizWarehouse._updateProductStock(product)
  182. return warehouse_stock_record
  183. # 出库
  184. @staticmethod
  185. def delivered(type,product,warehouse,count):
  186. ck_types = (WarehouseRecord.CK_ZJ,WarehouseRecord.CK_XS,WarehouseRecord.CK_PK)
  187. if count <= 0:
  188. raise CustomError(u'[%s]出库数量不能小于等于0' % product.name)
  189. if type not in ck_types:
  190. raise CustomError(u'[%s]出库类型错误' % product.name)
  191. stock_records = WarehouseStockRecord.objects.filter(
  192. product=product,
  193. warehouse=warehouse,
  194. surplus_count__gt=0
  195. ).order_by('entry_time')
  196. warehouse_record = WarehouseRecord.objects.create(
  197. type=type,
  198. product=product,
  199. warehouse=warehouse,
  200. count=-count,
  201. amount=0,
  202. amount2=0
  203. )
  204. BizWarehouse._deliveredForStockRecords(warehouse_record,stock_records)
  205. warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
  206. warehouse_stock.last_deliverd_time = timezone.now()
  207. product.last_deliverd_time = timezone.now()
  208. BizWarehouse._updateStock(warehouse_stock,warehouse_record)
  209. BizWarehouse._updateProductStock(product)
  210. BizWarehouse.updateRedundant(warehouse_record)
  211. return warehouse_record
  212. # 根据指定库存出库
  213. @staticmethod
  214. def deliveredByStockRecord(type,stock_record,count):
  215. product = stock_record.product
  216. warehouse = stock_record.warehouse
  217. if count <= 0:
  218. raise CustomError(u'[%s]出库数量不能小于等于0' % product.name)
  219. if count > stock_record.surplus_count:
  220. raise CustomError(u'[%s]出库数量超出对应入库记录的剩余数量' % product.name)
  221. warehouse_record = WarehouseRecord.objects.create(
  222. type=type,
  223. product=product,
  224. warehouse=warehouse,
  225. count=-count,
  226. amount=0,
  227. amount2=0
  228. )
  229. stock_records = (stock_record,)
  230. BizWarehouse._deliveredForStockRecords(warehouse_record, stock_records)
  231. warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
  232. warehouse_stock.last_deliverd_time = timezone.now()
  233. product.last_deliverd_time = timezone.now()
  234. BizWarehouse._updateStock(warehouse_stock, warehouse_record)
  235. BizWarehouse._updateProductStock(product)
  236. BizWarehouse.updateRedundant(warehouse_record)
  237. return warehouse_record
  238. # 退料
  239. @staticmethod
  240. def deliveredBack(warehouse_record, count):
  241. if count <= 0:
  242. raise CustomError(u'[%s]退料数量不能小于等于0' % warehouse_record.product.name)
  243. data = BizWarehouse._getDeliveredBackDetail(warehouse_record, count)
  244. res = BizWarehouse._deliveredBackByDetails(WarehouseRecord.TL,warehouse_record,data)
  245. BizWarehouse.updateRedundant(res)
  246. return res
  247. # 退货
  248. @staticmethod
  249. def entryBack(stock_record,count):
  250. product = stock_record.product
  251. warehouse = stock_record.warehouse
  252. if count <= 0:
  253. raise CustomError(u'[%s]退货数量不能小于等于0' % product.name)
  254. if count > stock_record.surplus_count:
  255. raise CustomError(u'[%s]退货数量超出对应入库记录的剩余数量' % product.name)
  256. warehouse_record = WarehouseRecord.objects.create(
  257. type=WarehouseRecord.TH,
  258. product=product,
  259. warehouse=warehouse,
  260. count=-count,
  261. amount=0,
  262. amount2=0
  263. )
  264. stock_records = (stock_record,)
  265. BizWarehouse._deliveredForStockRecords(warehouse_record, stock_records)
  266. warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
  267. BizWarehouse._updateStock(warehouse_stock, warehouse_record)
  268. BizWarehouse._updateProductStock(product)
  269. BizWarehouse.updateRedundant(warehouse_record)
  270. return warehouse_record
  271. # 批量退货
  272. @staticmethod
  273. def entryBatchBack(product,warehouse,count,supplier=None):
  274. if count <= 0:
  275. raise CustomError(u'[%s]退货数量不能小于等于0' % product.name)
  276. stock_records = WarehouseStockRecord.objects.filter(
  277. product=product,
  278. warehouse=warehouse,
  279. surplus_count__gt=0
  280. )
  281. if supplier:
  282. stock_records = stock_records.filter(
  283. supplier=supplier
  284. )
  285. stock_records = stock_records.order_by('entry_time')
  286. warehouse_record = WarehouseRecord.objects.create(
  287. type=WarehouseRecord.TH,
  288. product=product,
  289. warehouse=warehouse,
  290. count=-count,
  291. amount=0,
  292. amount2=0
  293. )
  294. BizWarehouse._deliveredForStockRecords(warehouse_record, stock_records)
  295. warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
  296. BizWarehouse._updateStock(warehouse_stock, warehouse_record)
  297. BizWarehouse._updateProductStock(product)
  298. BizWarehouse.updateRedundant(warehouse_record)
  299. return warehouse_record
  300. @staticmethod
  301. def _getDeliveredBackDetail(warehouse_record, count):
  302. details = WarehouseRecordDetail.objects.filter(
  303. warehouse_record=warehouse_record
  304. ).order_by('-warehouse_stock_record__entry_time')
  305. data = []
  306. for detail in details:
  307. back_count = (WarehouseBackMap.objects.filter(delivered_detail=detail).aggregate(count_sum=Sum('back_count')))['count_sum'] or 0
  308. if back_count >= -detail.count:
  309. continue
  310. temp = -(detail.count + back_count)
  311. if temp >= count:
  312. cur_count = count
  313. else:
  314. cur_count = temp
  315. data.append({'delivered_detail': detail, 'back_count': cur_count})
  316. count -= cur_count
  317. if not count:
  318. break
  319. if count:
  320. raise CustomError(u'[%s]退料数量超出出库剩余数量' % warehouse_record.product.name)
  321. return data
  322. @staticmethod
  323. def _deliveredBackByDetails(type, warehouse_record, details):
  324. product = warehouse_record.product
  325. warehouse = warehouse_record.warehouse
  326. item = WarehouseRecord.objects.create(
  327. type=type,
  328. product=warehouse_record.product,
  329. warehouse=warehouse_record.warehouse,
  330. count=0,
  331. amount=0,
  332. amount2=0
  333. )
  334. for detail in details:
  335. warehouse_stock_record = detail['delivered_detail'].warehouse_stock_record
  336. back_detail = WarehouseRecordDetail.objects.create(
  337. warehouse_record=item,
  338. warehouse_stock_record=warehouse_stock_record,
  339. count=detail['back_count']
  340. )
  341. warehouse_stock_record.surplus_count += detail['back_count']
  342. warehouse_stock_record.save()
  343. WarehouseBackMap.objects.create(
  344. back_detail=back_detail,
  345. delivered_detail=detail['delivered_detail'],
  346. back_count=detail['back_count']
  347. )
  348. item.count += detail['back_count']
  349. sum_row = WarehouseRecordDetail.objects.filter(
  350. warehouse_record=item
  351. ).aggregate(
  352. sum_amount=Sum(F('count') * F('warehouse_stock_record__entry_price')),
  353. sum_amount2=Sum(F('count') * F('warehouse_stock_record__entry_price2'))
  354. )
  355. item.amount = sum_row['sum_amount']
  356. item.amount2 = sum_row['sum_amount2']
  357. item.save()
  358. warehouse_stock = WarehouseStock.getByWarehouseAndProduct(warehouse,product)
  359. BizWarehouse._updateStock(warehouse_stock, item)
  360. BizWarehouse._updateProductStock(product)
  361. return item
  362. @staticmethod
  363. def _deliveredForStockRecords(warehouse_record,stock_records):
  364. product = warehouse_record.product
  365. temp_count = -warehouse_record.count
  366. for row in stock_records:
  367. if row.surplus_count >= temp_count:
  368. cur_count = temp_count
  369. else:
  370. cur_count = row.surplus_count
  371. row.surplus_count -= cur_count
  372. row.save()
  373. WarehouseRecordDetail.objects.create(
  374. warehouse_record=warehouse_record,
  375. warehouse_stock_record=row,
  376. count=-cur_count
  377. )
  378. temp_count -= cur_count
  379. if not temp_count:
  380. break
  381. if temp_count:
  382. raise CustomError(u'[%s]库存不足' % product.name)
  383. sum_row = WarehouseRecordDetail.objects.filter(
  384. warehouse_record=warehouse_record
  385. ).aggregate(
  386. sum_amount=Sum(F('count') * F('warehouse_stock_record__entry_price')),
  387. sum_amount2=Sum(F('count') * F('warehouse_stock_record__entry_price2'))
  388. )
  389. warehouse_record.amount = sum_row['sum_amount']
  390. warehouse_record.amount2 = sum_row['sum_amount2']
  391. warehouse_record.save()
  392. @staticmethod
  393. def _updateStock(warehouse_stock,warehouse_record):
  394. warehouse_stock.count += warehouse_record.count
  395. warehouse_stock.amount += warehouse_record.amount
  396. warehouse_stock.amount2 += warehouse_record.amount2
  397. if warehouse_stock.count != 0:
  398. warehouse_stock.avg_cost_price = warehouse_stock.amount / warehouse_stock.count
  399. warehouse_stock.avg_cost_price2 = warehouse_stock.amount2 / warehouse_stock.count
  400. warehouse_record.cur_count = warehouse_stock.count
  401. warehouse_record.cur_amount = warehouse_stock.amount
  402. warehouse_record.cur_amount2 = warehouse_stock.amount2
  403. warehouse_record.save()
  404. warehouse_stock.save()
  405. @staticmethod
  406. def _updateProductStock(product):
  407. sum_row = WarehouseStock.objects.filter(
  408. product=product
  409. ).aggregate(
  410. count=Sum('count'),
  411. amount=Sum('amount'),
  412. amount2=Sum('amount2')
  413. )
  414. product.stock_count = sum_row['count']
  415. product.stock_amount = sum_row['amount']
  416. product.stock_amount2 = sum_row['amount2']
  417. if product.stock_count != 0:
  418. product.avg_cost_price = product.stock_amount / product.stock_count
  419. product.avg_cost_price2 = product.stock_amount2 / product.stock_count
  420. product.save()
  421. class GetWarehouseSrockRecord():
  422. @staticmethod
  423. def getRecord(product, warehouse, supplier=None):
  424. record_data = []
  425. record_rows = WarehouseStockRecord.objects.filter(warehouse_id=warehouse, product_id=product,
  426. surplus_count__gt=0)
  427. if supplier:
  428. record_rows = record_rows.filter(supplier_id=supplier)
  429. for record_row in record_rows:
  430. product_base = record_row.product
  431. if product_base.type == ProductBase.GOODS:
  432. g_row = GoodsGodownEntryDetail.objects.filter(stock_record_id=record_row.id).first()
  433. else:
  434. g_row = GodownEntryDetail.objects.filter(stock_record_id=record_row.id).first()
  435. if not g_row:
  436. g_row = InventoryDetail.objects.filter(warehouse_stock_record_id=record_row.id).first()
  437. record_item = {
  438. 'stock_id': record_row.id,
  439. 'no': g_row.main.no,
  440. 'entry_price': Formater.formatPriceShow(record_row.entry_price),
  441. 'surplus_count': Formater.formatCountShow(record_row.surplus_count),
  442. 'detail_id': g_row.id,
  443. 'notes': g_row.main.notes or ''
  444. }
  445. record_data.append(record_item)
  446. return record_data