class IncomeType(FakerMixin, CommonFieldMixin, DescriptionFieldMixin):
    name = models.CharField('收入类型', max_length=50)

    data_path = os.path.join(BASE_DIR, 'data/income_type.json')

    objects = models.Manager()
    enabled_objects = EnabledEntityManager()

    class Meta:
        verbose_name = '收入类型'
        verbose_name_plural = '收入类型'

    def __str__(self):
        return self.name
Exemple #2
0
class Stage(FakerMixin, CommonFieldMixin):
    name = models.CharField('阶段', max_length=100)

    objects = models.Manager()
    enabled_objects = EnabledEntityManager()

    data_path = os.path.join(BASE_DIR, 'data/case_stage.json')

    class Meta:
        verbose_name = '案件阶段'
        verbose_name_plural = '案件阶段'

    def __str__(self):
        return '{}'.format(self.name)
Exemple #3
0
class Category(FakerMixin, CommonFieldMixin):
    name = models.CharField('类别', max_length=100)
    parent = models.ForeignKey('self',
                               on_delete=models.CASCADE,
                               null=True,
                               blank=True)

    objects = models.Manager()
    enabled_objects = EnabledEntityManager()

    data_path = os.path.join(BASE_DIR, 'data/case_category.json')

    class Meta:
        verbose_name = '案件分类'
        verbose_name_plural = '案件分类'

    def __str__(self):
        return '{}'.format(self.name)

    @classmethod
    def get_choices(cls, parent=None):
        """
        生成带有一级optgroup的HTML select options数据
        :param parent: 用于筛选返回结果中的类别,支持list或者单个int值
        :return: 
        """
        choices = []
        for c in cls.enabled_objects.all():
            if c.parent is not None:
                continue
            if parent and (isinstance(parent, list) and c.id not in parent
                           or c.id != parent):
                # 筛选分类
                # 在Trademark/Pattern relatedView中会调用
                continue
            choices.append((c.name, [(child_c.id, child_c.name)
                                     for child_c in c.category_set.all()]))
        return choices
class Receipts(CommonFieldMixin, DescriptionFieldMixin):
    amount = models.DecimalField('已收款金额', max_digits=10, decimal_places=2)
    exchange_rate = models.DecimalField('收款汇率', max_digits=8, decimal_places=4)
    received_date = models.DateField('收款日期')

    currency = models.ForeignKey('base.Currency',
                                 verbose_name='收款货币',
                                 on_delete=models.SET_NULL,
                                 null=True)
    receivable = models.ForeignKey(Receivable,
                                   verbose_name='待收款账单',
                                   on_delete=models.SET_NULL,
                                   null=True)
    deposit = models.ForeignKey('income.Deposit',
                                verbose_name='客户预存款',
                                on_delete=models.SET_NULL,
                                null=True)

    objects = models.Manager()
    enabled_objects = EnabledEntityManager()

    modelform_class = 'sale.forms.ReceiptsModelForm'
    datatables_class = 'sale.datatables.ReceiptsDataTable'
    related_entity_config = {}

    class Meta:
        verbose_name = '已收款项'
        verbose_name_plural = '已收款项'

    def __str__(self):
        return '{}{}-{}'.format(self.currency_id, self.amount,
                                self.received_date)

    def get_absolute_url(self):
        return reverse('receipts:detail', kwargs={'receipts_id': self.id})

    def get_deletion_url(self):
        return reverse('receipts:disable', kwargs={'receipts_id': self.id})

    def get_deletion_success_url(self):
        return reverse('receipts:detail', kwargs={'receipts_id': self.id})

    @classmethod
    def get_related_entity_config(cls):
        if cls.related_entity_config is not None:
            return cls.related_entity_config

    def get_detail_info(self):
        detail_info = {}
        desc = OrderedDict()
        detail_info['title'] = '金额:{}'.format(self.amount)
        detail_info['sub_title'] = ''
        desc['收款汇率'] = self.exchange_rate or '未设置'
        desc['货币'] = self.currency.name_chs
        desc['收款日期'] = self.received_date or '未指定'
        desc['手续费'] = self.transfer_charge or '未指定'
        desc['所属待收账单'] = getattr(self.receivable, 'name', '未指定编号')

        detail_info['desc'] = desc
        detail_info['enabled'] = self.enabled

        return detail_info

    @cached_property
    def amount_cny(self):
        if self.currency_id == 'CNY':
            return self.amount
        else:
            return self.amount * self.exchange_rate
class Receivable(CommonFieldMixin, DescriptionFieldMixin):
    no = models.CharField('待收款账单编号', max_length=100)
    sent_date = models.DateField('账单发送日期', null=True, blank=True)
    due_date = models.DateField('待收期限', null=True, blank=True)
    amount = models.DecimalField('待收总金额', max_digits=10, decimal_places=2)
    unsettled_amount = models.DecimalField('未收总金额',
                                           max_digits=10,
                                           decimal_places=2,
                                           null=True,
                                           blank=True)
    settled = models.BooleanField('客户是否付清', default=False)

    subcase = models.ForeignKey('case.SubCase',
                                verbose_name='关联分案',
                                on_delete=models.SET_NULL,
                                null=True)
    currency = models.ForeignKey('base.Currency',
                                 verbose_name='货币',
                                 on_delete=models.SET_NULL,
                                 null=True)

    objects = models.Manager()
    enabled_objects = EnabledEntityManager()

    modelform_class = 'sale.forms.ReceivableModelForm'
    datatables_class = 'sale.datatables.ReceivableDataTable'
    related_entity_config = {
        'sale.receipts': {
            'query_path': 'receivable',
            'verbose_name': '已收款项'
        }
    }

    class Meta:
        verbose_name = '待收款项'
        verbose_name_plural = '待收款项'

    def __str__(self):
        return '{}-{}{}'.format(self.no, self.currency_id, self.amount)

    def get_absolute_url(self):
        return reverse('receivable:detail', kwargs={'receivable_id': self.id})

    def get_deletion_url(self):
        return reverse('receivable:disable', kwargs={'receivable_id': self.id})

    def get_deletion_success_url(self):
        return reverse('receivable:detail', kwargs={'receivable_id': self.id})

    @classmethod
    def get_related_entity_config(cls):
        if cls.related_entity_config is not None:
            return cls.related_entity_config

    def get_detail_info(self):
        detail_info = {}
        desc = OrderedDict()
        detail_info['title'] = self.no or '未指定编号'
        detail_info['sub_title'] = '<a href="{}">{}</a>'.format(
            reverse('subcase:detail', kwargs={'subcase_id': self.subcase_id}),
            self.subcase.name)
        desc['所属案件'] = '<a href="{}">{}</a>'.format(
            reverse('case:detail', kwargs={'case_id': self.subcase.case_id}),
            self.subcase.case.name)
        desc['金额'] = self.amount
        desc['未收金额'] = self.unsettled_amount
        desc['货币'] = self.currency.name_chs
        desc['收款期限'] = self.due_date or '未指定'
        desc['账单发送日期'] = self.sent_date or '未指定'
        detail_info['desc'] = desc
        detail_info['enabled'] = self.enabled

        return detail_info
class Income(CommonFieldMixin, DescriptionFieldMixin):
    amount = models.DecimalField('收入金额', max_digits=10, decimal_places=2)
    exchange_rate = models.DecimalField('汇率',
                                        max_digits=8,
                                        decimal_places=4,
                                        default=Decimal('1'))
    incurred_date = models.DateField('收入日期', null=True, blank=True)

    currency = models.ForeignKey('base.Currency',
                                 verbose_name='货币',
                                 default='CNY',
                                 on_delete=models.SET_NULL,
                                 null=True)
    income_type = models.ForeignKey(IncomeType,
                                    verbose_name='收入类型',
                                    on_delete=models.SET_NULL,
                                    null=True)
    subcase = models.ForeignKey('case.SubCase',
                                verbose_name='关联分案件',
                                on_delete=models.SET_NULL,
                                null=True)

    objects = models.Manager()
    enabled_objects = EnabledEntityManager()

    modelform_class = 'income.forms.IncomeModelForm'
    datatables_class = 'income.datatables.IncomeDataTable'
    related_entity_config = {}

    class Meta:
        verbose_name = '其它收入'
        verbose_name_plural = '其它收入'

    def __str__(self):
        return '{}{}'.format(self.currency_id, self.amount)

    def get_absolute_url(self):
        return reverse('income:detail', kwargs={'income_id': self.id})

    def get_deletion_url(self):
        return reverse('income:disable', kwargs={'income_id': self.id})

    def get_deletion_success_url(self):
        return reverse('income:detail', kwargs={'income_id': self.id})

    @classmethod
    def get_related_entity_config(cls):
        if cls.related_entity_config is not None:
            return cls.related_entity_config

    def get_detail_info(self):
        detail_info = {}
        desc = OrderedDict()
        detail_info['title'] = '金额:{}'.format(self.amount)
        detail_info['sub_title'] = self.income_type.name
        desc['货币'] = self.currency.name_chs
        desc['汇率'] = self.exchange_rate or '未设置'
        desc['日期'] = self.incurred_date or '未指定'
        if self.subcase:
            desc['关联分案件'] = '<a href="{}">{}</a>'.format(
                reverse('subcase:detail',
                        kwargs={'subcase_id': self.subcase_id}),
                self.subcase.name)

        detail_info['desc'] = desc
        detail_info['enabled'] = self.enabled

        return detail_info

    @cached_property
    def amount_cny(self):
        if self.currency_id == 'CNY':
            return self.amount
        else:
            return self.amount * self.exchange_rate
Exemple #7
0
class Payment(CommonFieldMixin, DescriptionFieldMixin):
    amount = models.DecimalField('已付款金额', max_digits=10, decimal_places=2)
    exchange_rate = models.DecimalField('付款汇率', max_digits=8, decimal_places=4)
    paid_date = models.DateField('付款日期')

    currency = models.ForeignKey(
        'base.Currency',
        verbose_name='付款货币',
        on_delete=models.SET_NULL,
        null=True
    )
    payable = models.ForeignKey(
        Payable,
        verbose_name='应付款账单',
        on_delete=models.SET_NULL,
        null=True
    )

    objects = models.Manager()
    enabled_objects = EnabledEntityManager()

    modelform_class = 'purchase.forms.PaymentModelForm'
    datatables_class = 'purchase.datatables.PaymentDataTable'
    related_entity_config = {
        'purchase.paymentlink': {
            'query_path': 'payment',
            'verbose_name': '转移'
        }
    }

    class Meta:
        verbose_name = '已付款项'
        verbose_name_plural = '已付款项'

    def __str__(self):
        return '{}{}-{}'.format(self.currency_id, self.amount, self.paid_date)

    def get_absolute_url(self):
        return reverse('payment:detail', kwargs={'payment_id': self.id})

    def get_deletion_url(self):
        return reverse('payment:disable', kwargs={'payment_id': self.id})

    def get_deletion_success_url(self):
        return reverse('payment:detail', kwargs={'payment_id': self.id})

    @classmethod
    def get_related_entity_config(cls):
        if cls.related_entity_config is not None:
            return cls.related_entity_config

    def get_detail_info(self):
        detail_info = {}
        desc = OrderedDict()
        detail_info['title'] = '金额:{}'.format(self.amount)
        detail_info['sub_title'] = ''
        desc['付款汇率'] = self.exchange_rate or '未设置'
        desc['货币'] = self.currency.name_chs
        desc['付款日期'] = self.paid_date or '未指定'
        desc['手续费(人民币)'] = self.transfer_charge.amount or '未指定'
        desc['已转移金额'] = self.linked_amount
        desc['未转移金额'] = self.unlinked_amount
        desc['所属待付账单'] = '<a href="{}">{}</a>'.format(
            reverse('payable:detail', kwargs={'payable_id': self.payable_id}),
            self.payable.no
        )
        desc['所属分案'] = '<a href="{}">{}</a>'.format(
            reverse('subcase:detail', kwargs={'subcase_id': self.payable.subcase_id}),
            self.payable.subcase.name
        )
        desc['所属案件'] = '<a href="{}">{}</a>'.format(
            reverse('case:detail', kwargs={'case_id': self.payable.subcase.case_id}),
            self.payable.subcase.case.name
        )

        detail_info['desc'] = desc
        detail_info['enabled'] = self.enabled

        return detail_info

    @cached_property
    def amount_cny(self):
        if self.currency_id == 'CNY':
            return self.amount
        else:
            return self.amount * self.exchange_rate

    @cached_property
    def linked_amount(self):
        return sum(link.amount for link in self.paymentlink_set.filter(enabled=1).all())

    @cached_property
    def unlinked_amount(self):
        return self.amount - self.linked_amount

    @cached_property
    def unlinked_amount_cny(self):
        return self.unlinked_amount * self.exchange_rate
Exemple #8
0
class PaymentLink(CommonFieldMixin, DescriptionFieldMixin):
    amount = models.DecimalField('转移金额', max_digits=10, decimal_places=2)

    payment = models.ForeignKey(
        Payment,
        verbose_name='已付账单',
        on_delete=models.SET_NULL,
        null=True
    )
    subcase = models.ForeignKey(
        'case.SubCase',
        verbose_name='转移目标(分案件)',
        on_delete=models.SET_NULL,
        null=True
    )

    objects = models.Manager()
    enabled_objects = EnabledEntityManager()

    modelform_class = 'purchase.forms.PaymentLinkModelForm'
    datatables_class = 'purchase.datatables.PaymentLinkDataTable'
    related_entity_config = {}

    class Meta:
        verbose_name = '已付款转移'
        verbose_name_plural = '已付款转移'

    def __str__(self):
        return '转移已付款项: {}'.format(self.amount)

    def get_absolute_url(self):
        return reverse('paymentlink:detail', kwargs={'paymentlink_id': self.id})

    def get_deletion_url(self):
        return reverse('paymentlink:disable', kwargs={'paymentlink_id': self.id})

    def get_deletion_success_url(self):
        return reverse('paymentlink:detail', kwargs={'paymentlink_id': self.id})

    @classmethod
    def get_related_entity_config(cls):
        if cls.related_entity_config is not None:
            return cls.related_entity_config

    def get_detail_info(self):
        detail_info = {}
        desc = OrderedDict()
        detail_info['title'] = '金额:{}'.format(self.amount)
        detail_info['sub_title'] = ''
        desc['关联已付款金额'] = '<a href="{}">{}</a>'.format(
            reverse('payment:detail', kwargs={'payment_id': self.payment_id}),
            self.payment.amount
        )
        desc['关联已付款货币'] = self.payment.currency.name_chs
        desc['关联分案'] = '<a href="{}">{}</a>'.format(
            reverse('subcase:detail', kwargs={'subcase_id': self.subcase_id}),
            self.subcase.name
        )

        detail_info['desc'] = desc
        detail_info['enabled'] = self.enabled

        return detail_info

    @cached_property
    def amount_cny(self):
        if self.payment.currency_id == 'CNY':
            return self.amount
        else:
            return self.amount * self.payment.exchange_rate
Exemple #9
0
class Expense(CommonFieldMixin, DescriptionFieldMixin):
    amount = models.DecimalField('支出金额', max_digits=10, decimal_places=2)
    exchange_rate = models.DecimalField(
        '汇率',
        max_digits=8,
        decimal_places=4,
        default=Decimal('1'),
    )
    incurred_date = models.DateField('支出日期', null=True, blank=True)

    currency = models.ForeignKey('base.Currency',
                                 verbose_name='货币',
                                 default='CNY',
                                 on_delete=models.SET_NULL,
                                 null=True)
    expense_type = models.ForeignKey(ExpenseType,
                                     verbose_name='支出类型',
                                     on_delete=models.SET_NULL,
                                     null=True)
    subcase = models.ForeignKey('case.SubCase',
                                verbose_name='关联分案件',
                                on_delete=models.SET_NULL,
                                null=True)
    payment = models.OneToOneField('purchase.Payment',
                                   verbose_name='关联已付款项',
                                   related_name='transfer_charge',
                                   on_delete=models.SET_NULL,
                                   null=True)
    receipts = models.OneToOneField('sale.Receipts',
                                    verbose_name='关联已收款项',
                                    related_name='transfer_charge',
                                    on_delete=models.SET_NULL,
                                    null=True)

    objects = models.Manager()
    enabled_objects = EnabledEntityManager()

    modelform_class = 'expense.forms.ExpenseModelForm'
    datatables_class = 'expense.datatables.ExpenseDataTable'
    related_entity_config = {}

    class Meta:
        verbose_name = '支出'
        verbose_name_plural = '支出'

    def __str__(self):
        return '{}{}'.format(self.currency_id, self.amount)

    def get_absolute_url(self):
        return reverse('expense:detail', kwargs={'expense_id': self.id})

    def get_deletion_url(self):
        return reverse('expense:disable', kwargs={'expense_id': self.id})

    def get_deletion_success_url(self):
        return reverse('expense:detail', kwargs={'expense_id': self.id})

    @classmethod
    def get_related_entity_config(cls):
        if cls.related_entity_config is not None:
            return cls.related_entity_config

    def get_detail_info(self):
        detail_info = {}
        desc = OrderedDict()
        detail_info['title'] = '金额:{}'.format(self.amount)
        detail_info['sub_title'] = self.expense_type.name
        desc['货币'] = self.currency.name_chs
        desc['汇率'] = self.exchange_rate or '未设置'
        desc['日期'] = self.incurred_date or '未指定'
        if self.receipts:
            desc['关联已收款项'] = '<a href="{}">{} {}(<i>{}</i>)</a>'.format(
                reverse('receipts:detail',
                        kwargs={'receipts_id':
                                self.receipts_id}), self.receipts.currency_id,
                self.receipts.amount, self.receipts.received_date)
        if self.payment:
            desc['关联已付款项'] = '<a href="{}">{} {}(<i>{}</i>)</a>'.format(
                reverse('payment:detail',
                        kwargs={'payment_id':
                                self.payment_id}), self.payment.currency_id,
                self.payment.amount, self.payment.paid_date)
        if self.subcase:
            desc['关联分案件'] = '<a href="{}">{}</a>'.format(
                reverse('subcase:detail',
                        kwargs={'subcase_id': self.subcase_id}),
                self.subcase.name)

        detail_info['desc'] = desc
        detail_info['enabled'] = self.enabled

        return detail_info

    @cached_property
    def amount_cny(self):
        if self.currency_id == 'CNY':
            return self.amount
        else:
            return self.amount * self.exchange_rate
Exemple #10
0
class Case(FakerMixin, CommonFieldMixin, DescriptionFieldMixin):
    name = models.CharField('案件名称', max_length=200)
    archive_no = models.CharField('档案号', max_length=100, null=True, blank=True)
    closed = models.BooleanField('结案', default=False)

    client = models.ForeignKey('base.Client',
                               verbose_name='客户',
                               on_delete=models.SET_NULL,
                               null=True)
    owner = models.ForeignKey('base.Owner',
                              verbose_name='所属部门',
                              on_delete=models.SET_NULL,
                              null=True)
    category = models.ForeignKey(Category,
                                 verbose_name='案件类型',
                                 on_delete=models.SET_NULL,
                                 null=True)
    stage = models.ForeignKey(Stage,
                              verbose_name='所处阶段',
                              on_delete=models.SET_NULL,
                              null=True)
    trademark = models.ForeignKey('base.Trademark',
                                  verbose_name='商标',
                                  on_delete=models.SET_NULL,
                                  null=True,
                                  blank=True)
    pattern = models.ForeignKey('base.Pattern',
                                verbose_name='专利',
                                on_delete=models.SET_NULL,
                                null=True,
                                blank=True)

    objects = models.Manager()
    enabled_objects = EnabledEntityManager()

    datatables_class = 'case.datatables.CaseDataTable'
    modelform_class = 'case.forms.CaseModelForm'
    related_entity_config = {
        'case.subcase': {
            'query_path': 'case',
            'verbose_name': '分案信息',
        }
    }

    faker_fields = {
        'name': 'sentence',
        'archive_no': 'isbn13',
        'settled': 'pybool',
        'closed': 'pybool',
        'client': Client,
        'owner': Owner,
        'category': Category,
        'stage': Stage,
        'desc': 'paragraph'
    }

    class Meta:
        verbose_name = '案件'
        verbose_name_plural = '案件'

    def __str__(self):
        return '{}:{}'.format(self.archive_no, self.name)

    def get_absolute_url(self):
        return reverse('case:detail', kwargs={'case_id': self.id})

    def get_deletion_url(self):
        return reverse('case:disable', kwargs={'case_id': self.id})

    def get_deletion_success_url(self):
        return reverse('case:detail', kwargs={'case_id': self.id})

    def get_detail_info(self):
        detail_info = {}
        desc = OrderedDict()
        detail_info['title'] = self.name
        detail_info['sub_title'] = self.archive_no

        desc['客户'] = '<a href="{}">{}</a>'.format(
            reverse('client:detail', kwargs={'client_id': self.client_id}),
            self.client.name)
        desc['案件类型'] = self.category.name
        desc['所属阶段'] = self.stage.name
        desc['所属部门'] = self.owner.name
        desc['结案'] = '是' if self.closed else '否'
        detail_info['desc'] = desc
        detail_info['enabled'] = self.enabled

        return detail_info

    @classmethod
    def get_related_entity_config(cls):
        if cls.related_entity_config is not None:
            return cls.related_entity_config

    @cached_property
    def balance_amount_cny(self):
        balance = sum(subcase.receipts_sum_cny + subcase.income_sum_cny -
                      (subcase.payment_sum_cny + subcase.expense_sum_cny +
                       subcase.paymentlink_sum_cny)
                      for subcase in self.subcase_set.filter(enabled=1).all())

        return balance
Exemple #11
0
class SubCase(FakerMixin, CommonFieldMixin, DescriptionFieldMixin):
    name = models.CharField('分案名', max_length=200)
    closed = models.BooleanField('结案', default=False)

    agent = models.ForeignKey(
        'base.Client',
        related_name='agent_subcase',
        verbose_name='代理',
        on_delete=models.SET_NULL,
        null=True,
    )
    case = models.ForeignKey(
        Case,
        verbose_name='所属案件',
        on_delete=models.SET_NULL,
        null=True,
    )
    category = models.ForeignKey(Category,
                                 verbose_name='案件类型',
                                 on_delete=models.SET_NULL,
                                 null=True)
    stage = models.ForeignKey(
        Stage,
        verbose_name='所处阶段',
        on_delete=models.SET_NULL,
        null=True,
    )
    trademarknation = models.ForeignKey('base.TrademarkNation',
                                        verbose_name='商标-进入国家',
                                        on_delete=models.SET_NULL,
                                        null=True,
                                        blank=True)
    patternnation = models.ForeignKey('base.PatternNation',
                                      verbose_name='专利-进入国家',
                                      on_delete=models.SET_NULL,
                                      null=True,
                                      blank=True)

    objects = models.Manager()
    enabled_objects = EnabledEntityManager()

    modelform_class = 'case.forms.SubCaseModelForm'
    datatables_class = 'case.datatables.SubCaseDataTable'
    related_entity_config = {
        'sale.receivable': {
            'query_path': 'subcase',
            'verbose_name': '待收款项',
        },
        'purchase.payable': {
            'query_path': 'subcase',
            'verbose_name': '待付款项',
        },
        'expense.expense': {
            'query_path': 'subcase',
            'verbose_name': '其它支出'
        },
        'income.income': {
            'query_path': 'subcase',
            'verbose_name': '其它收入'
        },
    }

    class Meta:
        verbose_name = '分案'
        verbose_name_plural = '分案'

    def __str__(self):
        return '分案: {}'.format(self.name)

    def get_absolute_url(self):
        return reverse('subcase:detail', kwargs={'subcase_id': self.id})

    def get_deletion_url(self):
        return reverse('subcase:disable', kwargs={'subcase_id': self.id})

    def get_deletion_success_url(self):
        return reverse('subcase:detail', kwargs={'subcase_id': self.id})

    @classmethod
    def get_related_entity_config(cls):
        if cls.related_entity_config is not None:
            return cls.related_entity_config

    def get_detail_info(self):
        detail_info = {}
        desc = OrderedDict()
        detail_info['title'] = self.name
        detail_info['sub_title'] = '<a href="{}">{}</a>'.format(
            reverse('case:detail', kwargs={'case_id': self.case_id}),
            self.case.name)
        desc['代理'] = '<a href="{}">{}</a>'.format(
            reverse('client:detail', kwargs={'client_id': self.agent_id}),
            self.agent.name)
        desc['案件类型'] = self.category.name
        desc['所处阶段'] = self.stage.name
        if self.trademarknation:
            desc['商标(国家)'] = '<a href="{}">{}</a>'.format(
                reverse('trademarknation:detail',
                        kwargs={'trademarknation_id':
                                self.trademarknation_id}),
                self.trademarknation)
        if self.patternnation:
            desc['专利(国家)'] = '<a href="{}">{}</a>'.format(
                reverse('patternnation:detail',
                        kwargs={'patternnation_id': self.patternnation_id}),
                self.patternnation)
        desc['结案'] = '是' if self.closed else '否'
        detail_info['desc'] = desc
        detail_info['enabled'] = self.enabled

        return detail_info

    @property
    def receipts_iter(self):
        # 因为这个iter要多次使用
        # 所以不能设置为cached_property
        # 注意要使用filter enabled=1
        return itertools.chain.from_iterable(
            rv.receipts_set.filter(enabled=1).all()
            for rv in self.receivable_set.all())

    @cached_property
    def receipts_sum_cny(self):
        # 所有关联receipts的总金额
        # 减去transfer_charge
        return sum(rv.amount_cny - rv.transfer_charge.amount
                   for rv in self.receipts_iter)

    @property
    def payment_iter(self):
        # 因为这个iter要多次使用
        # 所以不能设置为cached_property
        # 注意要使用filter enabled=1
        return itertools.chain.from_iterable(
            pa.payment_set.filter(enabled=1).all()
            for pa in self.payable_set.all())

    @cached_property
    def payment_sum_cny(self):
        # 所有关联payment的总金额
        # 包含transfer_charge
        # 注意使用的是unlinked_amount_cny,表示当前payment未被转移的金额(CNY)
        return sum(pm.unlinked_amount_cny + pm.transfer_charge.amount
                   for pm in self.payment_iter)

    @cached_property
    def paymentlink_sum_cny(self):
        # 注意要使用filter enabled=1
        return sum(plink.amount_cny
                   for plink in self.paymentlink_set.filter(enabled=1).all())

    @cached_property
    def income_sum_cny(self):
        # 注意要使用filter enabled=1
        return sum(income.amount_cny
                   for income in self.income_set.filter(enabled=1).all())

    @cached_property
    def expense_sum_cny(self):
        # 注意要使用filter enabled=1
        return sum(expense.amount_cny
                   for expense in self.expense_set.filter(enabled=1).all())