0

我有这个模型,我可以节省应该在稍后阶段计费的费用。由于几个模型可以产生费用,我建立了一个通用关系。

class Fee(models.Model):
    content_type = models.ForeignKey(ContentType, blank=True, null=True,
                                     related_name='fees', on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField(null=True, blank=True, db_index=True)
    content_object = GenericForeignKey()

    class FeeType(models.IntegerChoices):
        SALES_CREDIT = 1, 'Sales Credit'
        MANAGEMENT = 2, 'Management Fee'
        BROKERAGE = 3, 'Brokerage Fee'
        PLATFORM = 4, 'Platform Fee'
        MARKETING = 5, 'Marketing Fee'
        DISCOUNT = 6, 'Discount'
        NOFEE = 99, 'No Fee'

    fee_type = models.IntegerField(choices=FeeType.choices)

    fee_date = models.DateField(auto_now_add=True)
    due_date = models.DateField(editable=False)

    creditor = models.ForeignKey('users.Company', on_delete=models.CASCADE, related_name='fee_credit')
    debitor = models.ForeignKey('users.Company', on_delete=models.CASCADE, related_name='fee_debit')

    quantity = models.PositiveBigIntegerField()
    rate = models.DecimalField(max_digits=10, decimal_places=4)

    class RateType(models.IntegerChoices):
        PERCENTAGE = 1, 'Percentage'
        NOMINAL = 2, 'Nominal'

    rate_type = models.IntegerField(choices=RateType.choices, default=1)

    taxes_due = models.BooleanField(default=True)
    including_taxes = models.BooleanField(default=True)
    tax_rate = models.DecimalField(max_digits=4, decimal_places=2)

    total_amount = models.DecimalField(max_digits=12, decimal_places=2, editable=False)
    total_taxes = models.DecimalField(max_digits=12, decimal_places=2, editable=False)
    total_amount_incl_taxes = models.DecimalField(max_digits=12, decimal_places=2, editable=False)

    billed = models.BooleanField(default=False)
    paid = models.BooleanField(default=False)

我有一个生活在芹菜任务中的函数,它准备将数据保存到所述模型中:

@shared_task
def log_fee(model, fee_type, creditor_pk, debitor_pk, quantity, rate_type, rate=None,
            taxes_due=True, including_taxes=True, tax_rate=None, fee_date=None):
    from fees.models import Fee
    from users.models import Company

    logger.info(f'{model} {fee_type} {creditor_pk} {debitor_pk} {quantity} {rate_type} {rate} {taxes_due} {including_taxes} {tax_rate}')

    # Get Model Details to link to
    app_label = model.get('app')
    model_name = model.get('object')
    model_pk = model.get('model_pk')

    # Catch fee types that are strings (most likely product related)
    logger.info(type(fee_type))
    if type(fee_type) == str:
        mapping = {'SC': 1, 'DC': 6, 'MF': 2, 'NP': 99}
        fee_type = mapping.get(fee_type)
        if fee_type == 99:
            rate = 0.0
            rate_type = 2

    logger.info(f'{fee_type} {rate} {rate_type}')

    # Get Object
    Object = apps.get_model(app_label=app_label, model_name=model_name)

    # Get Object instance
    try:
        target = Object.objects.get(pk=model_pk)
    except Object.DoesNotExist:
        target = None

    logger.info(target)

    # Get Involved Parties
    debitor = Company.objects.get(pk=debitor_pk)
    creditor = Company.objects.get(pk=creditor_pk)

    logger.info(debitor)
    logger.info(creditor)

    # Clean Fields when fee type == 1
    if fee_type == 1:
        # CALCULATE AMOUNTS AND TAXES
        total_amt = quantity * decimal.Decimal(rate)
        logger.info(total_amt)

        # Total Amount
        if rate_type == 1:
            total_amt = total_amt / 100

        # Set Tax Rate to Companies VAT if not already set.
        if not tax_rate:
            tax_rate = decimal.Decimal(creditor.vat)
            logger.info(tax_rate)

        if taxes_due:
            if including_taxes:
                total_taxes = total_amt - (total_amt / (1 + (tax_rate / 100)))
                total_amt = total_amt - total_taxes

            else:
                total_taxes = total_amt * (tax_rate / 100)
        else:
            total_taxes = 0.0

        total_amount = round(total_amt, 2)
        total_taxes = round(total_taxes, 2)
        total_amount_incl_taxes = total_amount + total_taxes

        # SET DUE DATE
        if not fee_date:
            fee_date = timezone.now().date()
        due_date = utils.get_valid_shifted_date(fee_date, days=creditor.payment_term)

        # Save Fee
        fee = Fee(content_object=target, fee_type=fee_type, creditor=creditor, debitor=debitor,
                  quantity=quantity, rate=rate, rate_type=rate_type, taxes_due=taxes_due,
                  including_taxes=including_taxes, tax_rate=tax_rate, due_date=due_date,
                  total_amount=total_amount, total_taxes=total_taxes, total_amount_incl_taxes=total_amount_incl_taxes)

        try:
            with transaction.atomic():
                fee.save()
        except Exception as e:
            logger.info(e)

如果我尝试使用 transaction.atomic() 保存模型,我会收到一条错误消息,说明'NoneType' object has no attribute 'pk'. 如果我省略 transaction.atomic() 语句,一切都会按预期保存。我几乎尝试了一切,但到目前为止没有成功。所有输入参数都采用模型期望的格式,正如我所说,没有 transaction.atomic() 保存工作。尽管如此,忽略错误消息,我还是觉得很不舒服。

知道这里发生了什么吗?

PS:我没有在目标模型(我正在测试的模型)上设置 GenericRelation,并且在运行 makemigrations 时,GenericRelation 无论如何都没有列出(这也让我感到困惑)。

我使用 Django 3.2 版和 PostgreSQL 作为数据库。

4

0 回答 0