models.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. from django.db import models
  2. from bound.models import BoundBatchModel,BoundDetailModel,OutBatchModel,BoundListModel,BoundListModel
  3. from django.utils import timezone
  4. from django.db.models.signals import pre_save, post_save
  5. from django.dispatch import receiver
  6. from decimal import Decimal
  7. from django.db.models import Sum
  8. from datetime import timedelta
  9. from django.db.models import Q
  10. import logging
  11. logger = logging.getLogger(__name__)
  12. # Create your models here.
  13. # 主表:托盘数据
  14. class ContainerListModel(models.Model):
  15. CONTAINER_STATUS = (
  16. (0, '空置'),
  17. (1, '入库中'),
  18. (2, '在库'),
  19. (3, '出库中'),
  20. (4, '已出库'),
  21. (5, '空托盘组')
  22. )
  23. container_code = models.IntegerField( verbose_name='托盘编号',unique=True)
  24. current_location = models.CharField(max_length=50, verbose_name='当前库位', default='N/A')
  25. target_location = models.CharField(max_length=50, verbose_name='目标库位', default='N/A')
  26. available= models.BooleanField(default=True, verbose_name='可用')
  27. status = models.IntegerField(choices=CONTAINER_STATUS, default=0, verbose_name='托盘状态')
  28. last_operation = models.DateTimeField(auto_now=True, verbose_name='最后操作时间')
  29. class Meta:
  30. db_table = 'container_list'
  31. verbose_name = 'ContainerList'
  32. verbose_name_plural = "ContainerList"
  33. ordering = ['-container_code']
  34. class ContainerDetailModel(models.Model):
  35. BATCH_STATUS=(
  36. (0, '空盘'),
  37. (1, '组盘'),
  38. (2, '在库'),
  39. (3, '已出库')
  40. )
  41. BATCH_CLASS = (
  42. (1, '成品'),
  43. (2, '空盘'),
  44. (3, '散盘'),
  45. )
  46. month = models.IntegerField(verbose_name='月份')
  47. container = models.ForeignKey(ContainerListModel, on_delete=models.CASCADE, related_name='details')
  48. batch = models.ForeignKey(BoundBatchModel, on_delete=models.CASCADE, verbose_name='批次',blank=True, null=True)
  49. goods_class = models.IntegerField(verbose_name='货品类别',choices=BATCH_CLASS, default=1)
  50. goods_code = models.CharField(max_length=50, verbose_name='货品编码')
  51. goods_desc = models.CharField(max_length=100, verbose_name='货品描述')
  52. goods_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'), verbose_name='数量')
  53. goods_out_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='出库数量')
  54. goods_weight = models.DecimalField(max_digits=10, decimal_places=3, verbose_name='重量')
  55. status = models.IntegerField(choices=BATCH_STATUS,default=0, verbose_name='状态')
  56. creater = models.CharField(max_length=50, verbose_name='创建人')
  57. create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
  58. update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
  59. is_delete = models.BooleanField(default=False, verbose_name='是否删除')
  60. class Meta:
  61. db_table = 'container_detail'
  62. verbose_name = 'ContainerDetail'
  63. verbose_name_plural = "ContainerDetail"
  64. ordering = ['-id']
  65. indexes = [
  66. models.Index(fields=['container']),
  67. models.Index(fields=['batch']),
  68. models.Index(fields=['goods_code']),
  69. models.Index(fields=['status']),
  70. ]
  71. def __str__(self):
  72. return f"{self.container.container_code} - {self.batch.bound_number if self.batch else 'N/A'} - {self.goods_code}"
  73. def save(self, *args, **kwargs):
  74. """
  75. 更新托盘上的物料数量,更新批次上的
  76. goods_in_qty(组盘数目)
  77. ,goods_in_location_qty(在库数目)
  78. ,goods_out_qty(出库数目)
  79. """
  80. # 在保存前调用信号处理器
  81. if self.pk:
  82. # 获取状态变化前的状态
  83. original = ContainerDetailModel.objects.get(pk=self.pk)
  84. original_status = original.status
  85. super().save(*args, **kwargs)
  86. # 更新批次数据
  87. if self.batch:
  88. # 避免循环更新 - 仅在数量相关字段变更时才更新
  89. if 'update_fields' not in kwargs or any(field in kwargs['update_fields'] for field in ['goods_qty', 'goods_out_qty','is_delete']):
  90. self.update_batch_stats()
  91. # # 根据出库数量更新状态
  92. # if self.goods_out_qty > self.goods_qty:
  93. # # 出库数量不能大于总数量
  94. # self.goods_out_qty = self.goods_qty
  95. # self.save(update_fields=['goods_out_qty'])
  96. # 根据当前数量状态更新状态
  97. if self.goods_out_qty >= self.goods_qty:
  98. self.status = 3 # 已出库
  99. super().save(*args, **kwargs)
  100. elif self.goods_qty - self.goods_out_qty > 0 and self.goods_out_qty > 0:
  101. self.status = 2 # 在库
  102. super().save(*args, **kwargs)
  103. if self.status == 3 and self.goods_qty - self.goods_out_qty > 0 :
  104. self.status = 2 # 在库
  105. super().save(*args, **kwargs)
  106. # 更新货物分类(关键修改点)
  107. if not kwargs.get('skip_class_update', False):
  108. # 获取托盘上所有物料(排除已删除和已出库的)
  109. details = ContainerDetailModel.objects.filter(
  110. container=self.container.id,
  111. is_delete=False
  112. ).exclude(status=3)
  113. # 统计不同批次数目
  114. batch_count = details.values('batch').distinct().count()
  115. new_class = 1 if batch_count == 1 else 3
  116. # 批量更新所有相关物料的goods_class
  117. details.exclude(goods_class=new_class).update(goods_class=new_class)
  118. # 如果当前物料的class需要更新
  119. if self.goods_class != new_class:
  120. self.goods_class = new_class
  121. super().save(update_fields=['goods_class'])
  122. def update_batch_stats(self):
  123. """更新批次的统计数据"""
  124. if not self.batch:
  125. return
  126. # 聚合托盘明细数据
  127. stats = ContainerDetailModel.objects.filter(
  128. batch=self.batch,
  129. is_delete=False
  130. ).aggregate(
  131. total_qty=models.Sum('goods_qty'),
  132. total_out_qty=models.Sum('goods_out_qty')
  133. )
  134. # 更新批次数据
  135. self.batch.goods_in_qty = stats['total_qty'] or 0
  136. self.batch.goods_in_location_qty = (stats['total_qty'] or 0) - (stats['total_out_qty'] or 0)
  137. self.batch.goods_out_qty = stats['total_out_qty'] or 0
  138. self.batch.save()
  139. def get_container_code(self):
  140. return self.container.container_code
  141. class ContainerDetailLogModel(models.Model):
  142. """托盘明细变更日志模型"""
  143. LOG_TYPES = (
  144. ('create', '创建'),
  145. ('update', '更新'),
  146. ('delete', '删除'),
  147. ('out', '出库'),
  148. ('cancel_out', '取消出库'),
  149. ('status_change', '状态变更'),
  150. )
  151. # 关联的托盘明细
  152. container_detail = models.ForeignKey(
  153. ContainerDetailModel,
  154. on_delete=models.CASCADE,
  155. related_name='logs'
  156. )
  157. # 日志类型
  158. log_type = models.CharField(
  159. max_length=20,
  160. choices=LOG_TYPES,
  161. verbose_name='日志类型'
  162. )
  163. # 原值
  164. old_goods_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='原数量', null=True, blank=True)
  165. old_goods_out_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='原出库数量', null=True, blank=True)
  166. old_status = models.IntegerField(
  167. choices=ContainerDetailModel.BATCH_STATUS,
  168. null=True,
  169. blank=True,
  170. verbose_name='原状态'
  171. )
  172. # 新值
  173. new_goods_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='新数量', null=True, blank=True)
  174. new_goods_out_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='新出库数量', null=True, blank=True)
  175. new_status = models.IntegerField(
  176. choices=ContainerDetailModel.BATCH_STATUS,
  177. null=True,
  178. blank=True,
  179. verbose_name='新状态'
  180. )
  181. # 元信息
  182. creater = models.CharField(max_length=50, verbose_name='操作人')
  183. create_time = models.DateTimeField(auto_now_add=True, verbose_name='操作时间')
  184. tobatchlog = models.BooleanField(default=False, verbose_name='是否转移到批次日志')
  185. class Meta:
  186. db_table = 'container_detail_log'
  187. verbose_name = '托盘明细变更日志'
  188. verbose_name_plural = "托盘明细变更日志"
  189. ordering = ['-create_time']
  190. def __str__(self):
  191. return f"{self.container_detail} - {self.get_log_type_display()} - {self.create_time.strftime('%Y-%m-%d %H:%M:%S')}"
  192. class batchLogModel(models.Model):
  193. """批次日志模型"""
  194. LOG_TYPES = (
  195. ('create', '创建'),
  196. ('update', '更新'),
  197. ('delete', '删除'),
  198. ('out', '出库'),
  199. ('cancel_out', '取消出库'),
  200. ('status_change', '状态变更'),
  201. )
  202. bound = models.ForeignKey(BoundListModel, on_delete=models.CASCADE, related_name='logs', null=True, blank=True)
  203. batch = models.ForeignKey(BoundBatchModel, on_delete=models.CASCADE, related_name='logs')
  204. log_type = models.CharField(max_length=20, choices=LOG_TYPES, verbose_name='日志类型')
  205. goods_code = models.CharField(max_length=50, verbose_name='货品编码')
  206. goods_desc = models.CharField(max_length=100, verbose_name='货品描述')
  207. goods_in_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='新入数量', null=True, blank=True)
  208. goods_out_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='新出数量', null=True, blank=True)
  209. detail_logs = models.ManyToManyField(
  210. ContainerDetailLogModel,
  211. related_name='batch_logs',
  212. verbose_name='关联托盘日志'
  213. )
  214. create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
  215. class Meta:
  216. db_table = 'batch_log_from_container_log'
  217. verbose_name = '批次日志'
  218. verbose_name_plural = "批次日志"
  219. ordering = ['-create_time']
  220. def __str__(self):
  221. return f"{self.batch} - {self.get_log_type_display()} - {self.create_time.strftime('%Y-%m-%d %H:%M:%S')}"
  222. def update_from_details(self):
  223. """从关联的托盘日志更新批次日志数据"""
  224. # 获取所有关联的托盘日志
  225. detail_logs = self.detail_logs.all()
  226. # 计算聚合值
  227. total_old_qty = detail_logs.aggregate(total=Sum('old_goods_qty'))['total'] or 0
  228. total_new_qty = detail_logs.aggregate(total=Sum('new_goods_qty'))['total'] or 0
  229. total_old_out = detail_logs.aggregate(total=Sum('old_goods_out_qty'))['total'] or 0
  230. total_new_out = detail_logs.aggregate(total=Sum('new_goods_out_qty'))['total'] or 0
  231. # 更新批次日志
  232. self.goods_in_qty = total_new_qty - total_old_qty
  233. self.goods_out_qty = total_new_out - total_old_out
  234. self.save()
  235. # 同时更新批次统计数据
  236. self.update_batch_stats()
  237. def update_batch_stats(self):
  238. """更新批次的统计数据"""
  239. # 聚合托盘明细数据
  240. stats = ContainerDetailModel.objects.filter(
  241. batch=self.batch,
  242. is_delete=False
  243. ).aggregate(
  244. total_qty=Sum('goods_qty'),
  245. total_out_qty=Sum('goods_out_qty')
  246. )
  247. # 更新批次数据
  248. self.batch.goods_in_qty = stats['total_qty'] or 0
  249. self.batch.goods_in_location_qty = (stats['total_qty'] or 0) - (stats['total_out_qty'] or 0)
  250. self.batch.goods_out_qty = stats['total_out_qty'] or 0
  251. self.batch.save()
  252. # 批次日志聚合处理器
  253. def aggregate_to_batch_log(container_log):
  254. """将托盘日志聚合到批次日志"""
  255. logger.info(f"开始聚合托盘日志: {container_log.id}")
  256. # 检查日志是否已处理
  257. if container_log.tobatchlog:
  258. logger.info(f"托盘日志 {container_log.id} 已处理过,跳过")
  259. return
  260. try:
  261. # 确定批次
  262. detail = container_log.container_detail
  263. if not detail:
  264. logger.warning(f"托盘日志 {container_log.id} 缺少关联的托盘明细")
  265. return
  266. batch = detail.batch
  267. if not batch:
  268. logger.warning(f"托盘明细 {detail.id} 缺少关联的批次")
  269. return
  270. # 确定操作类型
  271. log_type = container_log.log_type
  272. # 创建或获取批次日志(按类型和时间窗口分组)
  273. # 设置时间窗口(5分钟)
  274. time_window_start = container_log.create_time - timedelta(minutes=2)
  275. time_window_end = container_log.create_time + timedelta(minutes=2)
  276. # 查找相同批次、相同类型、在时间窗口内的批次日志
  277. existing_logs = batchLogModel.objects.filter(
  278. batch=batch,
  279. log_type=log_type,
  280. create_time__gte=time_window_start,
  281. create_time__lte=time_window_end
  282. ).order_by('create_time')
  283. if existing_logs.exists():
  284. # 使用时间窗口内最早的批次日志
  285. batch_log = existing_logs.first()
  286. logger.info(f"找到已有批次日志 {batch_log.id} 用于聚合")
  287. else:
  288. # 创建新的批次日志
  289. batch_log = batchLogModel.objects.create(
  290. batch=batch,
  291. log_type=log_type,
  292. goods_code=batch.goods_code,
  293. goods_desc=batch.goods_desc,
  294. create_time=container_log.create_time
  295. )
  296. logger.info(f"创建新批次日志 {batch_log.id}")
  297. # 将托盘日志关联到批次日志
  298. batch_log.detail_logs.add(container_log)
  299. # 从关联日志更新批次日志数据
  300. batch_log.update_from_details()
  301. # 根据日志类型添加关联单据信息
  302. if log_type == 'out' and not batch_log.bound:
  303. from bound.models import OutBoundDetailModel
  304. bound = OutBoundDetailModel.objects.filter(
  305. bound_batch_number=batch
  306. ).order_by('-create_time').first()
  307. if bound:
  308. batch_log.bound = bound.bound_list
  309. batch_log.save()
  310. logger.info(f"为批次日志 {batch_log.id} 添加出库单关联")
  311. if log_type == 'create' and not batch_log.bound:
  312. from bound.models import BoundDetailModel
  313. bound = BoundDetailModel.objects.filter(
  314. bound_batch=batch
  315. ).order_by('-create_time').first()
  316. if bound:
  317. batch_log.bound = bound.bound_list
  318. logger.info(f"为批次日志 {batch_log.id} 添加入库单关联")
  319. batch_log.save()
  320. # 标记日志已处理
  321. container_log.tobatchlog = True
  322. container_log.save()
  323. logger.info(f"成功聚合托盘日志 {container_log.id} 到批次日志 {batch_log.id}")
  324. return batch_log
  325. except Exception as e:
  326. logger.error(f"聚合托盘日志时出错: {e}", exc_info=True)
  327. raise
  328. # 简化的信号处理器
  329. @receiver(post_save, sender=ContainerDetailLogModel)
  330. def handle_container_detail_log(sender, instance, created, **kwargs):
  331. """创建托盘日志后立即关联到批次日志"""
  332. if created:
  333. try:
  334. aggregate_to_batch_log(instance)
  335. except Exception as e:
  336. print(f"Error aggregating log: {e}")
  337. @receiver(pre_save, sender=ContainerDetailModel)
  338. def container_detail_pre_save(sender, instance, **kwargs):
  339. """在托盘明细保存前记录变更"""
  340. if instance.pk:
  341. try:
  342. old_instance = ContainerDetailModel.objects.get(pk=instance.pk)
  343. logs = []
  344. # 数量变化日志
  345. if old_instance.goods_qty != instance.goods_qty:
  346. logs.append(ContainerDetailLogModel(
  347. container_detail=instance,
  348. log_type='update',
  349. old_goods_qty=old_instance.goods_qty,
  350. new_goods_qty=instance.goods_qty,
  351. creater=instance.creater
  352. ))
  353. # 出库数量变化日志
  354. if old_instance.goods_out_qty != instance.goods_out_qty:
  355. if old_instance.goods_out_qty < instance.goods_out_qty:
  356. log_type = 'out'
  357. else:
  358. log_type = 'cancel_out'
  359. logs.append(ContainerDetailLogModel(
  360. container_detail=instance,
  361. log_type=log_type,
  362. old_goods_out_qty=old_instance.goods_out_qty,
  363. new_goods_out_qty=instance.goods_out_qty,
  364. creater=instance.creater
  365. ))
  366. # 删除日志
  367. if old_instance.is_delete != instance.is_delete and instance.is_delete:
  368. logs.append(ContainerDetailLogModel(
  369. container_detail=instance,
  370. log_type='delete',
  371. old_goods_qty=old_instance.goods_qty,
  372. old_goods_out_qty=old_instance.goods_out_qty,
  373. new_goods_qty = old_instance.goods_qty,
  374. new_goods_out_qty = old_instance.goods_qty,
  375. old_status=old_instance.status,
  376. creater=instance.creater
  377. ))
  378. # 批量创建日志
  379. if logs:
  380. created_logs = ContainerDetailLogModel.objects.bulk_create(logs)
  381. for log in created_logs:
  382. # 由于bulk_create不会触发信号,我们手动调用信号处理函数
  383. handle_container_detail_log(ContainerDetailLogModel, log, created=True)
  384. except ContainerDetailModel.DoesNotExist:
  385. pass
  386. @receiver(post_save, sender=ContainerDetailModel)
  387. def container_detail_post_save(sender, instance, created, **kwargs):
  388. """在托盘明细新建时创建日志"""
  389. if created:
  390. ContainerDetailLogModel.objects.create(
  391. container_detail=instance,
  392. log_type='create',
  393. creater=instance.creater,
  394. new_goods_qty=instance.goods_qty,
  395. new_status=instance.status
  396. )
  397. # 明细表:操作记录 记录每次出入库的记录,使用goods来进行盘点,使用托盘码来进行托盘的操作记录
  398. class ContainerOperationModel(models.Model):
  399. OPERATION_TYPES = (
  400. ('container','组盘'),
  401. ('inbound', '入库'),
  402. ('outbound', '出库'),
  403. ('adjust', '调整'),
  404. )
  405. month = models.IntegerField(verbose_name='月份')
  406. container = models.ForeignKey(ContainerListModel, on_delete=models.CASCADE, related_name='operations')
  407. operation_type = models.CharField(max_length=20, choices=OPERATION_TYPES, verbose_name='操作类型')
  408. bound_id = models.IntegerField(verbose_name='出库申请', null=True, blank=True)
  409. batch = models.ForeignKey(BoundBatchModel, on_delete=models.CASCADE, verbose_name='批次',null=True, blank=True)
  410. goods_code = models.CharField(max_length=50, verbose_name='货品编码')
  411. goods_desc = models.CharField(max_length=100, verbose_name='货品描述')
  412. goods_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='数量')
  413. goods_weight = models.DecimalField(max_digits=10, decimal_places=3, verbose_name='重量')
  414. operator = models.CharField(max_length=50, verbose_name='操作人')
  415. timestamp = models.DateTimeField(auto_now_add=True, verbose_name='操作时间')
  416. from_location = models.CharField(max_length=50, null=True, verbose_name='原库位')
  417. to_location = models.CharField(max_length=50, null=True, verbose_name='目标库位')
  418. memo = models.TextField(null=True, verbose_name='备注')
  419. is_delete = models.BooleanField(default=False, verbose_name='是否删除')
  420. class Meta:
  421. db_table = 'container_operation'
  422. verbose_name = 'ContainerOperation'
  423. verbose_name_plural = "ContainerOperation"
  424. ordering = ['-timestamp']
  425. class ContainerWCSModel(models.Model):
  426. TASK_STATUS = (
  427. (100, '等待中'),
  428. (101, '处理中'),
  429. (102, '已暂停'),
  430. (103, '入库中'),
  431. (200, '已发送'),
  432. (300, '已完成'),
  433. (400, '失败'),
  434. )
  435. taskid = models.CharField(max_length=50, verbose_name='任务ID')
  436. batch = models.ForeignKey(BoundBatchModel, on_delete=models.CASCADE, verbose_name='关联批次',null=True, blank=True )
  437. batch_out = models.ForeignKey(OutBatchModel, on_delete=models.CASCADE, verbose_name='出库批次' , null=True, blank=True )
  438. bound_list = models.ForeignKey(BoundListModel, on_delete=models.CASCADE, verbose_name='关联出库单',null=True, blank=True )
  439. batch_number = models.CharField(max_length=50, verbose_name='批次号',null=True, blank=True )
  440. sequence = models.BigIntegerField(verbose_name='任务顺序')
  441. priority = models.IntegerField(default=100, verbose_name='优先级')
  442. month = models.IntegerField(verbose_name='月份')
  443. tasktype = models.CharField(max_length=50, verbose_name='任务类型')
  444. tasknumber = models.IntegerField(verbose_name='任务号',unique=True)
  445. order_number = models.IntegerField(verbose_name='c_number')
  446. container = models.CharField(max_length=50, verbose_name='托盘号')
  447. current_location = models.CharField(max_length=50, verbose_name='当前库位')
  448. target_location = models.CharField(max_length=50, verbose_name='目标库位')
  449. message = models.TextField(verbose_name='消息')
  450. working = models.IntegerField(default = 1,verbose_name='工作状态')
  451. status = models.IntegerField(choices=TASK_STATUS, default=100, verbose_name='状态')
  452. create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
  453. is_delete = models.BooleanField(default=False, verbose_name='是否删除')
  454. class Meta:
  455. db_table = 'container_wcs'
  456. verbose_name = 'ContainerWCS'
  457. verbose_name_plural = "ContainerWCS"
  458. ordering = ['-create_time']
  459. def to_dict(self):
  460. return {
  461. 'container': self.container,
  462. 'current_location': self.current_location,
  463. 'month' : self.month,
  464. 'target_location': self.target_location,
  465. 'tasktype': self.tasktype,
  466. 'taskid': self.taskid,
  467. 'taskNumber': self.tasknumber-20000000000,
  468. 'message': self.message,
  469. 'container': self.container,
  470. 'status': self.status
  471. }
  472. def __str__(self):
  473. return f"{self.taskid} - {self.get_status_display()}"
  474. # 这里的批次详情是主入库申请单下的子批次
  475. class TaskModel(models.Model):
  476. task_wcs = models.ForeignKey(ContainerWCSModel, on_delete=models.CASCADE, related_name='tasks')
  477. batch_detail = models.ForeignKey(BoundDetailModel, on_delete=models.CASCADE, verbose_name='批次详情')
  478. container_detail = models.ForeignKey(ContainerDetailModel, on_delete=models.CASCADE, verbose_name='托盘明细')
  479. class Meta:
  480. db_table = 'task'
  481. verbose_name = 'Task'
  482. verbose_name_plural = "Task"
  483. ordering = ['-id']
  484. class out_batch_detail(models.Model):
  485. out_bound = models.ForeignKey(BoundListModel, on_delete=models.CASCADE, related_name='out_batch_details')
  486. container = models.ForeignKey(ContainerListModel, on_delete=models.CASCADE, related_name='out_batch_details')
  487. container_detail = models.ForeignKey(ContainerDetailModel, on_delete=models.CASCADE, verbose_name='托盘明细')
  488. out_goods_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='数量')
  489. last_out_goods_qty = models.DecimalField(max_digits=10, decimal_places=3, default=Decimal('0'),verbose_name='上次出库数量')
  490. working = models.IntegerField(default = 1,verbose_name='工作状态')
  491. is_delete = models.BooleanField(default=False, verbose_name='是否删除')
  492. class Meta:
  493. db_table = 'out_batch_detail'
  494. verbose_name = 'OutBatchDetail'
  495. verbose_name_plural = "OutBatchDetail"
  496. ordering = ['container']