mirror of
https://github.com/himool/HimoolERP.git
synced 2024-09-20 14:56:00 +08:00
fix: 盘点
This commit is contained in:
parent
4424e1c3a4
commit
821666332f
|
@ -282,7 +282,7 @@ class ChargeOrderViewSet(BaseViewSet, ListModelMixin, RetrieveModelMixin, Create
|
||||||
return Response(data={'number': number}, status=status.HTTP_200_OK)
|
return Response(data={'number': number}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
@extend_schema(request=None, responses={200: ChargeOrderPermission})
|
@extend_schema(request=None, responses={200: ChargeOrderSerializer})
|
||||||
@action(detail=True, methods=['post'])
|
@action(detail=True, methods=['post'])
|
||||||
def void(self, request, *args, **kwargs):
|
def void(self, request, *args, **kwargs):
|
||||||
"""作废"""
|
"""作废"""
|
||||||
|
@ -328,7 +328,7 @@ class ChargeOrderViewSet(BaseViewSet, ListModelMixin, RetrieveModelMixin, Create
|
||||||
account.has_balance = account.balance_amount > 0
|
account.has_balance = account.balance_amount > 0
|
||||||
account.save(update_fields=['balance_amount', 'has_balance'])
|
account.save(update_fields=['balance_amount', 'has_balance'])
|
||||||
|
|
||||||
serializer = ChargeOrderPermission(instance=charge_order)
|
serializer = ChargeOrderSerializer(instance=charge_order)
|
||||||
return Response(data=serializer.data, status=status.HTTP_200_OK)
|
return Response(data=serializer.data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -169,9 +169,10 @@ class GoodsSerializer(BaseSerializer):
|
||||||
for batch_item in batch_items:
|
for batch_item in batch_items:
|
||||||
batch_initial_quantity = batch_item.get('initial_quantity', 0)
|
batch_initial_quantity = batch_item.get('initial_quantity', 0)
|
||||||
production_date = batch_item.get('production_date')
|
production_date = batch_item.get('production_date')
|
||||||
|
expiration_date = None
|
||||||
if production_date and goods.shelf_life_days:
|
if production_date and goods.shelf_life_days:
|
||||||
expiration_date = pendulum.parse(str(production_date)).add(days=goods.shelf_life_days)
|
expiration_date = pendulum.parse(str(production_date)) \
|
||||||
expiration_date = expiration_date.to_date_string()
|
.add(days=goods.shelf_life_days).to_date_string()
|
||||||
|
|
||||||
has_stock = batch_initial_quantity > 0
|
has_stock = batch_initial_quantity > 0
|
||||||
batchs.append(Batch(
|
batchs.append(Batch(
|
||||||
|
@ -236,10 +237,10 @@ class GoodsSerializer(BaseSerializer):
|
||||||
for batch_item in batch_items:
|
for batch_item in batch_items:
|
||||||
batch_initial_quantity = batch_item.get('initial_quantity', 0)
|
batch_initial_quantity = batch_item.get('initial_quantity', 0)
|
||||||
production_date = batch_item.get('production_date')
|
production_date = batch_item.get('production_date')
|
||||||
|
expiration_date = None
|
||||||
if production_date and goods.shelf_life_days:
|
if production_date and goods.shelf_life_days:
|
||||||
expiration_date = pendulum.parse(str(production_date)) \
|
expiration_date = pendulum.parse(str(production_date)) \
|
||||||
.add(days=goods.shelf_life_days)
|
.add(days=goods.shelf_life_days).to_date_string()
|
||||||
expiration_date = expiration_date.to_date_string()
|
|
||||||
|
|
||||||
if batch_id := batch_item.get('id'):
|
if batch_id := batch_item.get('id'):
|
||||||
batch = Batch.objects.filter(id=batch_id, warehouse=warehouse,
|
batch = Batch.objects.filter(id=batch_id, warehouse=warehouse,
|
||||||
|
|
|
@ -8,4 +8,5 @@ router.register('sales_reports', SalesReportViewSet, 'sales_report')
|
||||||
router.register('sales_hot_goods', SalesHotGoodsViewSet, 'sales_hot_goods')
|
router.register('sales_hot_goods', SalesHotGoodsViewSet, 'sales_hot_goods')
|
||||||
router.register('sales_trends', SalesTrendViewSet, 'sales_trend')
|
router.register('sales_trends', SalesTrendViewSet, 'sales_trend')
|
||||||
router.register('profit_trends', ProfitTrendViewSet, 'profit_trend')
|
router.register('profit_trends', ProfitTrendViewSet, 'profit_trend')
|
||||||
|
router.register('finance_statistics', FinanceStatisticViewSet, 'finance_statistic')
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
|
|
|
@ -11,6 +11,7 @@ from apps.statistic.schemas import *
|
||||||
from apps.statistic.models import *
|
from apps.statistic.models import *
|
||||||
from apps.purchase.models import *
|
from apps.purchase.models import *
|
||||||
from apps.sales.models import *
|
from apps.sales.models import *
|
||||||
|
from apps.finance.models import *
|
||||||
|
|
||||||
|
|
||||||
class PurchaseReportViewSet(BaseViewSet):
|
class PurchaseReportViewSet(BaseViewSet):
|
||||||
|
@ -201,7 +202,18 @@ class ProfitTrendViewSet(BaseViewSet, ListModelMixin):
|
||||||
return Response(data=queryset, status=status.HTTP_200_OK)
|
return Response(data=queryset, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class FinanceStatisticViewSet(FunctionViewSet):
|
||||||
|
"""收支统计"""
|
||||||
|
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
PaymentOrder.objects.filter().values('number', 'create_time', 'total_amount', suppliser_)
|
||||||
|
|
||||||
|
return Response()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'PurchaseReportViewSet', 'SalesReportViewSet',
|
'PurchaseReportViewSet', 'SalesReportViewSet',
|
||||||
'SalesHotGoodsViewSet', 'SalesTrendViewSet', 'ProfitTrendViewSet',
|
'SalesHotGoodsViewSet', 'SalesTrendViewSet', 'ProfitTrendViewSet',
|
||||||
|
'FinanceStatisticViewSet',
|
||||||
]
|
]
|
||||||
|
|
|
@ -87,8 +87,8 @@ class StockCheckBatch(Model):
|
||||||
related_name='stock_check_batchs', verbose_name='盘点单据')
|
related_name='stock_check_batchs', verbose_name='盘点单据')
|
||||||
stock_check_goods = ForeignKey('stock_check.StockCheckGoods', on_delete=CASCADE,
|
stock_check_goods = ForeignKey('stock_check.StockCheckGoods', on_delete=CASCADE,
|
||||||
related_name='stock_check_batchs', verbose_name='盘点商品')
|
related_name='stock_check_batchs', verbose_name='盘点商品')
|
||||||
batch = ForeignKey('goods.Batch', on_delete=CASCADE,
|
batch_number = CharField(max_length=32, verbose_name='批次编号')
|
||||||
related_name='stock_check_batchs', verbose_name='批次')
|
production_date = DateField(null=True, verbose_name='生产日期')
|
||||||
goods = ForeignKey('goods.Goods', on_delete=PROTECT,
|
goods = ForeignKey('goods.Goods', on_delete=PROTECT,
|
||||||
related_name='stock_check_batchs', verbose_name='商品')
|
related_name='stock_check_batchs', verbose_name='商品')
|
||||||
book_quantity = FloatField(verbose_name='账面数量')
|
book_quantity = FloatField(verbose_name='账面数量')
|
||||||
|
@ -98,7 +98,7 @@ class StockCheckBatch(Model):
|
||||||
team = ForeignKey('system.Team', on_delete=CASCADE, related_name='stock_check_batchs')
|
team = ForeignKey('system.Team', on_delete=CASCADE, related_name='stock_check_batchs')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = [('stock_check_goods', 'batch')]
|
unique_together = [('stock_check_goods', 'batch_number')]
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|
|
@ -16,18 +16,12 @@ class StockCheckOrderSerializer(BaseSerializer):
|
||||||
class StockCheckBatchItemSerializer(BaseSerializer):
|
class StockCheckBatchItemSerializer(BaseSerializer):
|
||||||
"""盘点批次"""
|
"""盘点批次"""
|
||||||
|
|
||||||
batch_number = CharField(source='batch.number', required=False, label='批次编号')
|
|
||||||
status_display = CharField(source='get_status_display', read_only=True, label='盘点状态')
|
status_display = CharField(source='get_status_display', read_only=True, label='盘点状态')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = StockCheckBatch
|
model = StockCheckBatch
|
||||||
read_only_fields = ['id', 'batch_number', 'book_quantity', 'surplus_quantity',
|
read_only_fields = ['id', 'book_quantity', 'surplus_quantity', 'status', 'status_display']
|
||||||
'status', 'status_display']
|
fields = ['batch_number', 'production_date', 'actual_quantity', *read_only_fields]
|
||||||
fields = ['batch', 'actual_quantity', *read_only_fields]
|
|
||||||
|
|
||||||
def validate_batch(self, instance):
|
|
||||||
instance = self.validate_foreign_key(Batch, instance, message='批次不存在')
|
|
||||||
return instance
|
|
||||||
|
|
||||||
def validate_actual_quantity(self, value):
|
def validate_actual_quantity(self, value):
|
||||||
if value < 0:
|
if value < 0:
|
||||||
|
@ -69,7 +63,8 @@ class StockCheckOrderSerializer(BaseSerializer):
|
||||||
raise ValidationError(f'商品[{goods.name}]批次为空')
|
raise ValidationError(f'商品[{goods.name}]批次为空')
|
||||||
|
|
||||||
total_actual_quantity = reduce(lambda total, item: NP.plus(total, item['actual_quantity']),
|
total_actual_quantity = reduce(lambda total, item: NP.plus(total, item['actual_quantity']),
|
||||||
stock_check_batch_items)
|
stock_check_batch_items, 0)
|
||||||
|
|
||||||
if total_actual_quantity != attrs['actual_quantity']:
|
if total_actual_quantity != attrs['actual_quantity']:
|
||||||
raise ValidationError(f'商品[{goods.name}]盘点数量错误')
|
raise ValidationError(f'商品[{goods.name}]盘点数量错误')
|
||||||
|
|
||||||
|
@ -101,8 +96,8 @@ class StockCheckOrderSerializer(BaseSerializer):
|
||||||
if not instance.is_active:
|
if not instance.is_active:
|
||||||
raise ValidationError(f'仓库[{instance.name}]未激活')
|
raise ValidationError(f'仓库[{instance.name}]未激活')
|
||||||
|
|
||||||
if instance.is_locked:
|
if not instance.is_locked:
|
||||||
raise ValidationError(f'仓库[{instance.name}]已锁定')
|
raise ValidationError(f'仓库[{instance.name}]未锁定')
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def validate_handler(self, instance):
|
def validate_handler(self, instance):
|
||||||
|
@ -154,9 +149,26 @@ class StockCheckOrderSerializer(BaseSerializer):
|
||||||
|
|
||||||
# 创建盘点批次
|
# 创建盘点批次
|
||||||
if goods.enable_batch_control:
|
if goods.enable_batch_control:
|
||||||
stock_check_batch_items = validated_data['stock_check_batchs']
|
for stock_check_batch_item in stock_check_goods_item['stock_check_batchs']:
|
||||||
for stock_check_batch_item in stock_check_batch_items:
|
batch_number = stock_check_batch_item['batch_number']
|
||||||
batch = stock_check_batch_item['batch']
|
batch = Batch.objects.filter(number=batch_number, warehouse=warehouse,
|
||||||
|
goods=goods, team=self.team).first()
|
||||||
|
|
||||||
|
if not batch:
|
||||||
|
production_date = stock_check_batch_item.get('production_date')
|
||||||
|
expiration_date = None
|
||||||
|
if production_date and goods.shelf_life_days:
|
||||||
|
expiration_date = pendulum.parse(str(production_date)) \
|
||||||
|
.add(days=goods.shelf_life_days).to_date_string()
|
||||||
|
|
||||||
|
inventory = Inventory.objects.get(warehouse=warehouse, goods=goods, team=self.team)
|
||||||
|
batch = Batch.objects.create(
|
||||||
|
number=batch_number, inventory=inventory, warehouse=warehouse, goods=goods,
|
||||||
|
total_quantity=0, remain_quantity=0, production_date=production_date,
|
||||||
|
shelf_life_days=goods.shelf_life_days, expiration_date=expiration_date,
|
||||||
|
has_stock=False, team=self.team
|
||||||
|
)
|
||||||
|
|
||||||
book_quantity = batch.remain_quantity
|
book_quantity = batch.remain_quantity
|
||||||
actual_quantity = stock_check_batch_item['actual_quantity']
|
actual_quantity = stock_check_batch_item['actual_quantity']
|
||||||
surplus_quantity = NP.minus(actual_quantity, book_quantity)
|
surplus_quantity = NP.minus(actual_quantity, book_quantity)
|
||||||
|
@ -170,8 +182,9 @@ class StockCheckOrderSerializer(BaseSerializer):
|
||||||
|
|
||||||
stock_check_batchs.append(StockCheckBatch(
|
stock_check_batchs.append(StockCheckBatch(
|
||||||
stock_check_order=stock_check_order, stock_check_goods=stock_check_goods,
|
stock_check_order=stock_check_order, stock_check_goods=stock_check_goods,
|
||||||
goods=goods, book_quantity=book_quantity, actual_quantity=actual_quantity,
|
batch_number=batch_number, goods=goods, book_quantity=book_quantity,
|
||||||
surplus_quantity=surplus_quantity, status=status, team=self.team
|
actual_quantity=actual_quantity, surplus_quantity=surplus_quantity,
|
||||||
|
status=status, team=self.team
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
StockCheckBatch.objects.bulk_create(stock_check_batchs)
|
StockCheckBatch.objects.bulk_create(stock_check_batchs)
|
||||||
|
|
|
@ -59,7 +59,8 @@ class StockCheckOrderViewSet(BaseViewSet, ListModelMixin, RetrieveModelMixin, Cr
|
||||||
# 同步批次
|
# 同步批次
|
||||||
if goods.enable_batch_control:
|
if goods.enable_batch_control:
|
||||||
for stock_check_batch in stock_check_goods.stock_check_batchs.all():
|
for stock_check_batch in stock_check_goods.stock_check_batchs.all():
|
||||||
batch = stock_check_batch.batch
|
batch = Batch.objects.get(number=stock_check_batch.batch_number, warehouse=warehouse,
|
||||||
|
goods=goods, team=self.team)
|
||||||
batch.remain_quantity = stock_check_batch.actual_quantity
|
batch.remain_quantity = stock_check_batch.actual_quantity
|
||||||
batch.has_stock = batch.remain_quantity > 0
|
batch.has_stock = batch.remain_quantity > 0
|
||||||
update_batchs.append(batch)
|
update_batchs.append(batch)
|
||||||
|
|
|
@ -194,6 +194,9 @@ class StockTransferOrderViewSet(BaseViewSet, ListModelMixin, RetrieveModelMixin,
|
||||||
stock_in_order.is_void = True
|
stock_in_order.is_void = True
|
||||||
stock_in_order.save(update_fields=['is_void'])
|
stock_in_order.save(update_fields=['is_void'])
|
||||||
|
|
||||||
|
serializer = StockTransferOrderSerializer(instance=stock_transfer_order)
|
||||||
|
return Response(data=serializer.data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'StockTransferOrderViewSet',
|
'StockTransferOrderViewSet',
|
||||||
|
|
Loading…
Reference in a new issue