class FundTransfer(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) account = models.ForeignKey(Account, on_delete=models.CASCADE) iban_beneficiary = IBANField() bic_beneficiary = models.CharField(max_length=11, blank=True) bank_beneficiary = models.CharField(max_length=200, blank=True) name_beneficiary = models.CharField(max_length=150, blank=True) amount = models.FloatField(default=0, validators=[MinValueValidator(0)]) amount_bgn = models.FloatField(default=0, validators=[MinValueValidator(0)]) currency = models.ForeignKey(Currency, on_delete=models.CASCADE, default=1) details = models.CharField(max_length=150) created = models.DateTimeField(auto_now_add=True) last_updated = models.DateTimeField(auto_now=True) status = models.CharField(max_length=10, choices=[(s.name, s.value) for s in TransferStatusEnum], default='I') otp_generated = models.CharField(max_length=10, blank=True) pin_otp = models.CharField(max_length=10, blank=True) user_approved = models.ForeignKey(User, related_name='ft_approval', on_delete=models.CASCADE, blank=True, null=True) reference_cbs = models.CharField(max_length=20, blank=True) payment_system = models.CharField(max_length=7, choices=[(p.name, p.value) for p in PaymentSystemEnum], default='I')
class CreditCard(models.Model): IBAN = IBANField(null=True, default=None) cardHolderName = models.CharField(max_length=25) def __str__(self): return "Carte N°" + str( self.id ) + " - Détenteur : " + self.cardHolderName + " | IBAN : " + self.iban
class Accounts(models.Model): CURRENCY = ( ('Dollars', 'Dollars'), ('Euro', 'Euro'), ('Lira', 'Lira'), ) the_owner = models.ForeignKey(User, on_delete=models.CASCADE) currency = models.CharField(max_length=50, choices=CURRENCY, null=True) account_name = models.CharField(primary_key=True, max_length=100) amount = models.IntegerField(default=0) iban = IBANField(enforce_database_constraint=True, unique=True) account_no = IBANField(enforce_database_constraint=True, unique=True) def __str__(self): return "Account Owner: {} / {} / {}".format(str(self.the_owner), str(self.iban), str(self.currency))
class CustomUser(AbstractUser): iban = IBANField(enforce_database_constraint=True, unique=True) created_by = models.ForeignKey( 'self', on_delete=models.SET_NULL, blank=True, null=True, ) REQUIRED_FIELDS = ['iban', 'first_name', 'last_name', 'email']
class Usuario(models.Model): owner = models.ForeignKey(User, on_delete=models.CASCADE, null=True) name = models.CharField(max_length=250, default="") surname = models.CharField(max_length=250, default="") iban = IBANField(enforce_database_constraint=True, unique=True) concatenado = models.CharField(max_length=200, default="") def __str__(self): return self.concatenado
class Bank_Account(models.Model): iban = IBANField() bank = models.CharField(max_length=15,choices=BANK_CHOICES) def get_absolute_url(self): return reverse('statement-detail', args=[str(self.iban)]) def __str__(self): return f'{self.bank}, {self.iban}'
class Account(models.Model): """ Bank account. All Fund transfers are initiated from an account """ DJANGO_BANK_BIC = 'DJNG8280' product = models.ForeignKey(AccountProduct, on_delete=models.CASCADE) customer = models.ForeignKey(Customer, on_delete=models.CASCADE, null=True) users = models.ManyToManyField(User) iban = IBANField(enforce_database_constraint=True, unique=True) balance = models.FloatField(validators=[MinValueValidator(0)], default=0) currency = models.ForeignKey(Currency, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) status = models.CharField(max_length=7, choices=[(s.name, s.value) for s in AccountStatusEnum], default='A') def __str__(self): return f"{self.iban} {self.currency.code}"
class UserForm(forms.ModelForm): name = forms.CharField(min_length=2, max_length=15) surname = forms.CharField(max_length=200) iban = IBANField(enforce_database_constraint=True, unique=True) concatenado = forms.CharField(max_length=200, widget=forms.HiddenInput, required=False) class Meta: model = Usuario fields = ('concatenado', 'name', 'surname', 'iban') def clean_name(self): name = self.cleaned_data.get('name') return name.upper() def clean_surname(self): surname = self.cleaned_data.get('surname') return surname.upper() def clean_iban(self): iban = self.cleaned_data.get('iban') if (Usuario.objects.filter(iban=iban)): raise forms.ValidationError("Error, " + iban + " is registred") return iban return iban def clean_email(self): email = self.cleaned_data.get('email') lista_usuarios = Usuario.objects.all() if (Usuario.objects.filter(email=email)): raise forms.ValidationError("Error, email " + email + " is registred") return email return email def clean(self): if (self.is_valid()): name = self.cleaned_data.get('name') surname = self.cleaned_data.get('surname') self.cleaned_data['concatenado'] = name + " " + surname
class Customer(models.Model): first_name = models.CharField(max_length=32, db_index=True) last_name = models.CharField(max_length=32, db_index=True) iban = IBANField( unique=True, help_text="Up to 34 chars bank account number, depends on country.") created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET(get_sentinel_user), related_name='customers_added') def __str__(self): return " ".join([self.first_name, self.last_name]) def created_by_email_display(self): if self.created_by == get_sentinel_user(): return '(DELETED)' return self.created_by.email created_by_email_display.short_description = 'created by' created_by_email_display.admin_order_field = 'created_by__email'
class MangoPayBankAccount(models.Model): mangopay_user = models.ForeignKey(MangoPayUser, related_name="mangopay_bank_accounts") mangopay_id = models.PositiveIntegerField(null=True, blank=True) iban = IBANField() bic = SWIFTBICField() address = models.CharField(max_length=254) def create(self): client = get_mangopay_api_client() mangopay_bank_account = BankAccount() mangopay_bank_account.UserId = self.mangopay_user.mangopay_id mangopay_bank_account.OwnerName = \ self.mangopay_user.user.get_full_name() mangopay_bank_account.OwnerAddress = self.address mangopay_bank_account.IBAN = self.iban mangopay_bank_account.BIC = self.bic created_bank_account = client.users.CreateBankAccount( str(self.mangopay_user.mangopay_id), mangopay_bank_account) self.mangopay_id = created_bank_account.Id self.save()
class Profile(models.Model): user = models.OneToOneField(User) type = models.CharField(max_length=20, choices=PROFILE_TYPE, default=FINAL) phone = models.CharField(max_length=20, validators=[validate_phone_number]) picture = models.URLField(null=True, blank=True) swift_bank_account = SWIFTBICField(null=True, blank=True) iban_bank_account = IBANField(null=True, blank=True) account_name = models.CharField(max_length=255, blank=True, null=True) documents = ArrayField(models.URLField(), null=True, blank=True, help_text=_("Separate urls using a comma")) work_days = ArrayField(models.IntegerField( validators=[MinValueValidator(0), MaxValueValidator(6)]), null=True, blank=True, help_text=_("Separate using a comma")) work_hours = ArrayField(models.IntegerField( validators=[MinValueValidator(0), MaxValueValidator(23)]), null=True, blank=True, help_text=_("Separate using a comma")) city = ArrayField(models.IntegerField(null=True, blank=True, validators=[validate_city]), null=True, blank=True, help_text=_("Separate using a comma")) rating = models.FloatField(default=0) fee = models.FloatField(default=0) language = models.CharField(max_length=7, default="en", blank=True, null=True) def __str__(self): return "%s - %s" % (self.user.email, self.type)
class Vendor(models.Model): rating = models.FloatField( null=True, blank=True, validators=[MaxValueValidator(5), MinValueValidator(1)]) IBAN = IBANField(null=True, default=None) user = models.OneToOneField(User, on_delete=models.CASCADE) # Param: upload_to='directory' photo = models.TextField(blank=True, null=True, max_length=None) no_rue = models.CharField(max_length=6) adresse = models.CharField(max_length=25) code_postal = models.PositiveIntegerField( validators=[MaxValueValidator(9999), MinValueValidator(1000)]) ville = models.CharField(max_length=25) pays = models.CharField(max_length=25) def __str__(self): if (self.user.first_name == ""): return self.user.username else: return self.user.first_name
class Invoice(models.Model): """ Model representing Invoice itself. It keeps all necessary information described at https://www.gov.uk/vat-record-keeping/vat-invoices """ COUNTER_PERIOD = Choices(('DAILY', _('daily')), ('MONTHLY', _('monthly')), ('YEARLY', _('yearly'))) TYPE = Choices(('INVOICE', _(u'Invoice')), ('ADVANCE', _(u'Advance invoice')), ('PROFORMA', _(u'Proforma invoice')), ('CREDIT_NOTE', _(u'Credit note'))) STATUS = Choices(('NEW', _(u'new')), ('SENT', _(u'sent')), ('RETURNED', _(u'returned')), ('CANCELED', _(u'canceled')), ('PAID', _(u'paid'))) PAYMENT_METHOD = Choices(('BANK_TRANSFER', _(u'bank transfer')), ('CASH', _(u'cash')), ('CASH_ON_DELIVERY', _(u'cash on delivery')), ('PAYMENT_CARD', _(u'payment card'))) DELIVERY_METHOD = Choices( ('PERSONAL_PICKUP', _(u'personal pickup')), ('MAILING', _(u'mailing')), ('DIGITAL', _(u'digital')), ) CONSTANT_SYMBOL = Choices( ('0001', _(u'0001 - Payments for goods based on legal and executable decision from legal authority' )), ('0008', _(u'0008 - Cashless payments for goods')), ('0038', _(u'0038 - Cashless funds for wages')), ('0058', _(u'0058 - Cashless penalty and delinquency charges')), ('0068', _(u'0068 - Transfer of funds for wages and other personal costs')), ('0138', _(u'0138 - Cashless deductions at source')), ('0168', _(u'0168 - Cashless payments in loans')), ('0178', _(u'0178 - Sales from provided services')), ('0298', _(u'0298 - Other cashless transfers')), ('0304', _(u'0304 - Prior payments for services')), ('0308', _(u'0308 - Cashless payments for services')), ('0358', _(u'0358 - Payments dedicated to payout through post offices')), ('0379', _(u'0379 - Other income, income from postal order')), ('0498', _(u'0498 - Payments in loans')), ('0558', _(u'0558 - Cashless other financial payments')), ('0934', _(u'0934 - Benefits - prior payments')), ('0968', _(u'0968 - Other cashless transfers')), ('1144', _(u'1144 - Prior payment - advance')), ('1148', _(u'1148 - Payment - current advance')), ('1744', _(u'1744 - Accounting of tax at income tax of physical body and corporate body' )), ('1748', _(u'1748 - Income tax of physical body and corporate body based on declared tax year' )), ('3118', _(u'3118 - Insurance and empl. contrib. to insur. co. and the Labor Office' )), ('3344', _(u'3344 - Penalty from message - prior')), ('3348', _(u'3348 - Penalty from message')), ('3354', _(u'3354 - Insurance payments by insurance companies')), ('3558', _(u'3558 - Cashless insurance payments by insurance companies')), ('8147', _(u'8147 - Payment (posted together with the instruction)'))) # General information type = models.CharField(_(u'type'), max_length=64, choices=TYPE, default=TYPE.INVOICE) sequence = models.IntegerField(_(u'sequence'), db_index=True, blank=True) number = models.CharField(_(u'number'), max_length=128, blank=True) status = models.CharField(_(u'status'), choices=STATUS, max_length=64, default=STATUS.NEW) subtitle = models.CharField(_(u'subtitle'), max_length=255, blank=True) related_document = models.CharField(_(u'related document'), max_length=100, blank=True) language = models.CharField(_(u'language'), max_length=10, choices=settings.LANGUAGES) note = models.CharField(_(u'note'), max_length=255, blank=True, default=_(u'Thank you for using our services.')) date_issue = models.DateField(_(u'issue date')) date_tax_point = models.DateField(_(u'tax point date'), help_text=_(u'time of supply')) date_due = models.DateField(_(u'due date'), help_text=_(u'payment till')) date_sent = MonitorField(monitor='status', when=[STATUS.SENT], verbose_name=_(u'date sent'), blank=True, null=True, default=None) date_paid = MonitorField(monitor='status', when=[STATUS.PAID], verbose_name=_(u'date paid'), blank=True, null=True, default=None) # Payment details currency = models.CharField(_(u'currency'), max_length=10, choices=CURRENCY_CHOICES) credit = models.DecimalField(_(u'credit'), max_digits=10, decimal_places=2, default=0) #already_paid = models.DecimalField(_(u'already paid'), max_digits=10, decimal_places=2, default=0) payment_method = models.CharField(_(u'payment method'), choices=PAYMENT_METHOD, max_length=64) constant_symbol = models.CharField(_(u'constant symbol'), max_length=64, choices=CONSTANT_SYMBOL, blank=True) variable_symbol = models.PositiveIntegerField( _(u'variable symbol'), validators=[MinValueValidator(0), MaxValueValidator(9999999999)], blank=True, null=True, default=None) specific_symbol = models.PositiveIntegerField( _(u'specific symbol'), validators=[MinValueValidator(0), MaxValueValidator(9999999999)], blank=True, null=True, default=None) reference = models.CharField(_(u'reference'), max_length=140, blank=True) bank_name = models.CharField(_(u'bank name'), max_length=255, blank=True) bank_street = models.CharField(_(u'bank street and number'), max_length=255, blank=True) bank_zip = models.CharField(_(u'bank ZIP'), max_length=255, blank=True) bank_city = models.CharField(_(u'bank city'), max_length=255, blank=True) bank_country = CountryField(_(u'bank country'), max_length=255, blank=True) bank_iban = IBANField(verbose_name=_(u'Account number (IBAN)'), default=None) bank_swift_bic = SWIFTBICField(verbose_name=_(u'Bank SWIFT / BIC'), default=None) # Issuer details supplier_name = models.CharField(_(u'supplier name'), max_length=255, default=None) supplier_street = models.CharField(_(u'supplier street and number'), max_length=255, blank=True) supplier_zip = models.CharField(_(u'supplier ZIP'), max_length=255, blank=True) supplier_city = models.CharField(_(u'supplier city'), max_length=255, blank=True) supplier_country = CountryField(_(u'supplier country'), default=None) supplier_registration_id = models.CharField(_(u'supplier Reg. No.'), max_length=255, blank=True) supplier_tax_id = models.CharField(_(u'supplier Tax No.'), max_length=255, blank=True) supplier_vat_id = VATField(_(u'supplier VAT No.'), blank=True) supplier_additional_info = JSONField( _(u'supplier additional information'), load_kwargs={'object_pairs_hook': OrderedDict}, blank=True, null=True, default=None) # for example www or legal matters # Contact details issuer_name = models.CharField(_(u'issuer name'), max_length=255, blank=True) issuer_email = models.EmailField(_(u'issuer email'), blank=True) issuer_phone = models.CharField(_(u'issuer phone'), max_length=255, blank=True) # Customer details customer_name = models.CharField(_(u'customer name'), max_length=255) customer_street = models.CharField(_(u'customer street and number'), max_length=255, blank=True) customer_zip = models.CharField(_(u'customer ZIP'), max_length=255, blank=True) customer_city = models.CharField(_(u'customer city'), max_length=255, blank=True) customer_country = CountryField(_(u'customer country')) customer_registration_id = models.CharField(_(u'customer Reg. No.'), max_length=255, blank=True) customer_tax_id = models.CharField(_(u'customer Tax No.'), max_length=255, blank=True) customer_vat_id = VATField(_(u'customer VAT No.'), blank=True) customer_additional_info = JSONField( _(u'customer additional information'), load_kwargs={'object_pairs_hook': OrderedDict}, blank=True, null=True, default=None) customer_email = models.EmailField(_(u'customer email'), blank=True) customer_phone = models.CharField(_(u'customer phone'), max_length=255, blank=True) # Shipping details shipping_name = models.CharField(_(u'shipping name'), max_length=255, blank=True) shipping_street = models.CharField(_(u'shipping street and number'), max_length=255, blank=True) shipping_zip = models.CharField(_(u'shipping ZIP'), max_length=255, blank=True) shipping_city = models.CharField(_(u'shipping city'), max_length=255, blank=True) shipping_country = CountryField(_(u'shipping country'), blank=True) # Delivery details delivery_method = models.CharField(_(u'delivery method'), choices=DELIVERY_METHOD, max_length=64, default=DELIVERY_METHOD.PERSONAL_PICKUP) # sums (auto calculated fields) total = models.DecimalField(_(u'total'), max_digits=10, decimal_places=2, blank=True, default=0) vat = models.DecimalField(_(u'VAT'), max_digits=10, decimal_places=2, blank=True, null=True, default=0) # Other attachments = JSONField(_(u'attachments'), load_kwargs={'object_pairs_hook': OrderedDict}, blank=True, null=True, default=None) created = models.DateTimeField(_(u'created'), auto_now_add=True) modified = models.DateTimeField(_(u'modified'), auto_now=True) objects = InvoiceQuerySet.as_manager() class Meta: db_table = 'invoicing_invoices' verbose_name = _(u'invoice') verbose_name_plural = _(u'invoices') ordering = ('date_issue', 'sequence') default_permissions = ('list', 'view', 'add', 'change', 'delete') def __str__(self): return self.number def __unicode__(self): return self.number @transaction.atomic def save(self, **kwargs): if self.sequence in EMPTY_VALUES: self.sequence = Invoice.get_next_sequence( self.type, self.date_issue, getattr(self, 'number_prefix', None)) if self.number in EMPTY_VALUES: self.number = self._get_number(getattr(self, 'number_format', None)) return super(Invoice, self).save(**kwargs) def get_absolute_url(self): return getattr( settings, 'INVOICING_INVOICE_ABSOLUTE_URL', lambda invoice: reverse('invoicing:invoice_detail', args=(invoice.pk, )))(self) @staticmethod def get_next_sequence(type, important_date, number_prefix=None): """ Returns next invoice sequence based on ``settings.INVOICING_COUNTER_PERIOD``. .. warning:: This is only used to prepopulate ``sequence`` field on saving new invoice. To get invoice sequence always use ``sequence`` field. .. note:: To get invoice number use ``number`` field. :return: string (generated next sequence) """ with transaction.atomic(): Invoice.objects.lock() invoice_counter_reset = getattr(settings, 'INVOICING_COUNTER_PERIOD', Invoice.COUNTER_PERIOD.YEARLY) if invoice_counter_reset == Invoice.COUNTER_PERIOD.DAILY: relative_invoices = Invoice.objects.filter( date_issue=important_date) elif invoice_counter_reset == Invoice.COUNTER_PERIOD.YEARLY: relative_invoices = Invoice.objects.filter( date_issue__year=important_date.year) elif invoice_counter_reset == Invoice.COUNTER_PERIOD.MONTHLY: relative_invoices = Invoice.objects.filter( date_issue__year=important_date.year, date_issue__month=important_date.month) else: raise ImproperlyConfigured( "INVOICING_COUNTER_PERIOD can be set only to these values: DAILY, MONTHLY, YEARLY." ) invoice_counter_per_type = getattr(settings, 'INVOICING_COUNTER_PER_TYPE', False) if invoice_counter_per_type: relative_invoices = relative_invoices.filter(type=type) if number_prefix is not None: relative_invoices = relative_invoices.filter( number__startswith=number_prefix) start_from = getattr(settings, 'INVOICING_NUMBER_START_FROM', 1) last_sequence = relative_invoices.aggregate( Max('sequence'))['sequence__max'] or start_from - 1 return last_sequence + 1 def _get_number(self, number_format=None): """ Generates on the fly invoice number from template provided by ``settings.INVOICING_NUMBER_FORMAT``. ``Invoice`` object is provided as ``invoice`` variable to the template, therefore all object fields can be used to generate number format. .. warning:: This is only used to prepopulate ``number`` field on saving new invoice. To get invoice number always use ``number`` field. :return: string (generated number) """ if not number_format: number_format = getattr( settings, "INVOICING_NUMBER_FORMAT", "{{ invoice.date_tax_point|date:'Y' }}/{{ invoice.sequence }}") return Template(number_format).render(Context({'invoice': self})) def get_tax_rate(self): if self.taxation_policy: # There is taxation policy -> get tax rate customer_country_code = self.customer_country.code if self.customer_country else None supplier_country_code = self.supplier_country.code if self.supplier_country else None return self.taxation_policy.get_tax_rate(self.customer_vat_id, customer_country_code, supplier_country_code) else: # If there is not any special taxation policy, set default tax rate return TaxationPolicy.get_default_tax() @property def taxation_policy(self): taxation_policy = getattr(settings, 'INVOICING_TAXATION_POLICY', None) if taxation_policy is not None: return import_name(taxation_policy) # Check if supplier is from EU if self.supplier_country: if EUTaxationPolicy.is_in_EU(self.supplier_country.code): return EUTaxationPolicy return None @property def is_overdue(self): return self.date_due < now().date() and self.status not in [ self.STATUS.PAID, self.STATUS.CANCELED ] @property def overdue_days(self): return (now().date() - self.date_due).days @property def payment_term(self): return (self.date_due - self.date_issue).days if self.total > 0 else 0 def set_supplier_data(self, supplier): self.supplier_name = supplier.get('name') self.supplier_street = supplier.get('street', '') self.supplier_zip = supplier.get('zip', '') self.supplier_city = supplier.get('city', '') self.supplier_country = supplier.get('country_code') self.supplier_registration_id = supplier.get('registration_id', '') self.supplier_tax_id = supplier.get('tax_id', '') self.supplier_vat_id = supplier.get('vat_id', '') self.supplier_additional_info = supplier.get('additional_info', None) bank = supplier.get('bank') self.bank_name = bank.get('name') self.bank_street = bank.get('street') self.bank_zip = bank.get('zip') self.bank_city = bank.get('city') self.bank_country = bank.get('country_code') self.bank_iban = bank.get('iban') self.bank_swift_bic = bank.get('swift_bic') def set_customer_data(self, customer): self.customer_name = customer.get('name') self.customer_street = customer.get('street', '') self.customer_zip = customer.get('zip', '') self.customer_city = customer.get('city', '') self.customer_country = customer.get('country_code') self.customer_registration_id = customer.get('registration_id', '') self.customer_tax_id = customer.get('tax_id', '') self.customer_vat_id = customer.get('vat_id', '') self.customer_additional_info = customer.get('additional_info', None) def set_shipping_data(self, shipping): self.shipping_name = shipping.get('name', '') self.shipping_street = shipping.get('street', '') self.shipping_zip = shipping.get('zip', '') self.shipping_city = shipping.get('city', '') self.shipping_country = shipping.get('country_code', '') # http://www.superfaktura.sk/blog/neplatca-dph-vzor-faktury/ def is_supplier_vat_id_visible(self): is_supplier_vat_id_visible = getattr( settings, 'INVOICING_IS_SUPPLIER_VAT_ID_VISIBLE', None) if is_supplier_vat_id_visible is not None: return is_supplier_vat_id_visible(self) if self.vat is None and self.supplier_country == self.customer_country: return False # VAT is not 0 if self.vat != 0 or self.item_set.filter(tax_rate__gt=0).exists(): return True # VAT is 0, check if customer is from EU and from same country as supplier is_EU_customer = EUTaxationPolicy.is_in_EU( self.customer_country.code) if self.customer_country else False return is_EU_customer and self.supplier_country != self.customer_country @property def vat_summary(self): #rates_and_sum = self.item_set.all().annotate(base=Sum(F('qty')*F('price_per_unit'))).values('tax_rate', 'base') #rates_and_sum = self.item_set.all().values('tax_rate').annotate(Sum('price_per_unit')) #rates_and_sum = self.item_set.all().values('tax_rate').annotate(Sum(F('qty')*F('price_per_unit'))) from django.db import connection cursor = connection.cursor() cursor.execute( 'select tax_rate as rate, SUM(quantity*unit_price*(100-discount)/100) as base, ROUND(CAST(SUM(quantity*unit_price*((100-discount)/100)*(tax_rate/100)) AS numeric), 2) as vat from invoicing_items where invoice_id = %s group by tax_rate;', [self.pk]) desc = cursor.description return [ dict(zip([col[0] for col in desc], row)) for row in cursor.fetchall() ] @cached_property def has_discount(self): if not self.item_set.exists(): return False discounts = list(set(self.item_set.values_list('discount', flat=True))) return len(discounts) > 1 or discounts[0] > 0 @cached_property def has_unit(self): if not self.item_set.exists(): return False units = list(set(self.item_set.values_list('unit', flat=True))) return len(units) > 1 or units[0] != Item.UNIT_EMPTY @cached_property def max_quantity(self): quantity = self.item_set.aggregate(Max('quantity')) return quantity.get('quantity__max', 1) if quantity else 1 @property def subtotal(self): sum = 0 for item in self.item_set.all(): sum += item.subtotal return round(sum, 2) def calculate_vat(self): if len(self.vat_summary) == 1 and self.vat_summary[0]['vat'] is None: return None vat = 0 for vat_rate in self.vat_summary: vat += vat_rate['vat'] or 0 return vat def calculate_total(self): # TODO: save into model field on post_save signal for InvoiceItem #total = self.subtotal + self.vat # subtotal with vat total = 0 for vat_rate in self.vat_summary: total += float(vat_rate['vat'] or 0) + float(vat_rate['base']) #total *= float((100 - float(self.discount)) / 100) # subtract discount amount total -= float(self.credit) # subtract credit #total -= self.already_paid # subtract already paid return round(total, 2)
class Invoice(models.Model): COUNTER_PERIOD = Choices(('DAILY', _('daily')), ('MONTHLY', _('monthly')), ('YEARLY', _('yearly'))) TYPE = Choices(('INVOICE', _(u'Invoice')), ('ADVANCE', _(u'Advance invoice')), ('PROFORMA', _(u'Proforma invoice')), ('VAT_CREDIT_NOTE', _(u'VAT credit note'))) STATUS = Choices(('NEW', _(u'new')), ('SENT', _(u'sent')), ('RETURNED', _(u'returned')), ('CANCELED', _(u'canceled')), ('PAID', _(u'paid'))) PAYMENT_METHOD = Choices(('BANK_TRANSFER', _(u'bank transfer')), ('CASH', _(u'cash')), ('CASH_ON_DELIVERY', _(u'cash on delivery')), ('PAYMENT_CARD', _(u'payment card'))) DELIVERY_METHOD = Choices(('PERSONAL_PICKUP', _(u'personal pickup')), ('MAILING', _(u'mailing'))) CONSTANT_SYMBOL = Choices( ('0001', _(u'0001 - Payments for goods based on legal and executable decision from legal authority' )), ('0008', _(u'0008 - Cashless payments for goods')), ('0038', _(u'0038 - Cashless funds for wages')), ('0058', _(u'0058 - Cashless penalty and delinquency charges')), ('0068', _(u'0068 - Transfer of funds for wages and other personal costs')), ('0138', _(u'0138 - Cashless deductions at source')), ('0168', _(u'0168 - Cashless payments in loans')), ('0178', _(u'0178 - Sales from provided services')), ('0298', _(u'0298 - Other cashless transfers')), ('0304', _(u'0304 - Prior payments for services')), ('0308', _(u'0308 - Cashless payments for services')), ('0358', _(u'0358 - Payments dedicated to payout through post offices')), ('0379', _(u'0379 - Other income, income from postal order')), ('0498', _(u'0498 - Payments in loans')), ('0558', _(u'0558 - Cashless other financial payments')), ('0934', _(u'0934 - Benefits - prior payments')), ('0968', _(u'0968 - Other cashless transfers')), ('1144', _(u'1144 - Prior payment - advance')), ('1148', _(u'1148 - Payment - current advance')), ('1744', _(u'1744 - Accounting of tax at income tax of physical body and corporate body' )), ('1748', _(u'1748 - Income tax of physical body and corporate body based on declared tax year' )), ('3118', _(u'3118 - Insurance and empl. contrib. to insur. co. and the Labor Office' )), ('3344', _(u'3344 - Penalty from message - prior')), ('3354', _(u'3354 - Insurance payments by insurance companies')), ('3558', _(u'3558 - Cashless insurance payments by insurance companies')), ('8147', _(u'8147 - Payment (posted together with the instruction)'))) # General information type = models.CharField(_(u'type'), max_length=64, choices=TYPE, default=TYPE.INVOICE) number = models.IntegerField(_(u'number'), db_index=True, blank=True) full_number = models.CharField(max_length=128, blank=True) status = models.CharField(_(u'status'), choices=STATUS, max_length=64, default=STATUS.NEW) subtitle = models.CharField(_(u'subtitle'), max_length=255, blank=True, null=True, default=None) language = models.CharField(_(u'language'), max_length=2, choices=settings.LANGUAGES) note = models.CharField(_(u'note'), max_length=255, blank=True, null=True, default=_(u'Thank you for using our services.')) date_issue = models.DateField(_(u'issue date')) date_tax_point = models.DateField(_(u'tax point date')) # time of supply date_due = models.DateField(_(u'due date')) # payment till date_sent = MonitorField(monitor='status', when=[STATUS.SENT], blank=True, null=True, default=None) # Payment details currency = models.CharField(_(u'currency'), max_length=10, choices=CURRENCY_CHOICES) discount = models.DecimalField(_(u'discount (%)'), max_digits=3, decimal_places=1, default=0) credit = models.DecimalField(_(u'credit'), max_digits=10, decimal_places=2, default=0) #already_paid = models.DecimalField(_(u'already paid'), max_digits=10, decimal_places=2, default=0) payment_method = models.CharField(_(u'payment method'), choices=PAYMENT_METHOD, max_length=64) constant_symbol = models.CharField(_(u'constant symbol'), max_length=64, choices=CONSTANT_SYMBOL, blank=True, null=True, default=None) variable_symbol = models.PositiveIntegerField(_(u'variable symbol'), max_length=10, blank=True, null=True, default=None) specific_symbol = models.PositiveIntegerField(_(u'specific symbol'), max_length=10, blank=True, null=True, default=None) reference = models.CharField(_(u'reference'), max_length=140, blank=True, null=True, default=None) bank_name = models.CharField(_(u'bank name'), max_length=255, blank=True, null=True, default=lambda: default_supplier('bank.name')) bank_street = models.CharField( _(u'bank street and number'), max_length=255, blank=True, null=True, default=lambda: default_supplier('bank.street')) bank_zip = models.CharField(_(u'bank ZIP'), max_length=255, blank=True, null=True, default=lambda: default_supplier('bank.zip')) bank_city = models.CharField(_(u'bank city'), max_length=255, blank=True, null=True, default=lambda: default_supplier('bank.city')) bank_country = CountryField( _(u'bank country'), max_length=255, blank=True, null=True, default=lambda: default_supplier('bank.country_code')) bank_iban = IBANField(verbose_name=_(u'Account number (IBAN)'), default=lambda: default_supplier('bank.iban')) bank_swift_bic = SWIFTBICField( verbose_name=_(u'Bank SWIFT / BIC'), default=lambda: default_supplier('bank.swift_bic')) # Issuer details supplier_name = models.CharField(_(u'supplier name'), max_length=255, default=lambda: default_supplier('name')) supplier_street = models.CharField( _(u'supplier street and number'), max_length=255, blank=True, null=True, default=lambda: default_supplier('street')) supplier_zip = models.CharField(_(u'supplier ZIP'), max_length=255, blank=True, null=True, default=lambda: default_supplier('zip')) supplier_city = models.CharField(_(u'supplier city'), max_length=255, blank=True, null=True, default=lambda: default_supplier('city')) supplier_country = CountryField( _(u'supplier country'), default=lambda: default_supplier('country_code')) supplier_registration_id = models.CharField( _(u'supplier Reg. No.'), max_length=255, blank=True, null=True, default=lambda: default_supplier('registration_id')) supplier_tax_id = models.CharField( _(u'supplier Tax No.'), max_length=255, blank=True, null=True, default=lambda: default_supplier('tax_id')) supplier_vat_id = VATField(_(u'supplier VAT No.'), blank=True, null=True, default=lambda: default_supplier('vat_id')) supplier_additional_info = JSONField( _(u'supplier additional information'), load_kwargs={'object_pairs_hook': OrderedDict}, blank=True, null=True, default=lambda: default_supplier('additional_info' )) # for example www or legal matters # Contact details issuer_name = models.CharField(_(u'issuer name'), max_length=255, blank=True, null=True, default=None) issuer_email = models.EmailField(_(u'issuer email'), blank=True, null=True, default=None) issuer_phone = models.CharField(_(u'issuer phone'), max_length=255, blank=True, null=True, default=None) # Customer details customer_name = models.CharField(_(u'customer name'), max_length=255) customer_street = models.CharField(_(u'customer street and number'), max_length=255, blank=True, null=True, default=None) customer_zip = models.CharField(_(u'customer ZIP'), max_length=255, blank=True, null=True, default=None) customer_city = models.CharField(_(u'customer city'), max_length=255, blank=True, null=True, default=None) customer_country = CountryField(_(u'customer country')) customer_registration_id = models.CharField(_(u'customer Reg. No.'), max_length=255, blank=True, null=True, default=None) customer_tax_id = models.CharField(_(u'customer Tax No.'), max_length=255, blank=True, null=True, default=None) customer_vat_id = VATField(_(u'customer VAT ID'), blank=True, null=True, default=None) customer_additional_info = JSONField( _(u'customer additional information'), load_kwargs={'object_pairs_hook': OrderedDict}, blank=True, null=True, default=None) # Shipping details shipping_name = models.CharField(_(u'shipping name'), max_length=255, blank=True, null=True, default=None) shipping_street = models.CharField(_(u'shipping street and number'), max_length=255, blank=True, null=True, default=None) shipping_zip = models.CharField(_(u'shipping ZIP'), max_length=255, blank=True, null=True, default=None) shipping_city = models.CharField(_(u'shipping city'), max_length=255, blank=True, null=True, default=None) shipping_country = CountryField(_(u'shipping country'), blank=True, null=True, default=None) # Delivery details delivery_method = models.CharField(_(u'delivery method'), choices=DELIVERY_METHOD, max_length=64, default=DELIVERY_METHOD.PERSONAL_PICKUP) # Other created = models.DateTimeField(_(u'created'), auto_now_add=True) modified = models.DateTimeField(_(u'modified'), auto_now=True) class Meta: db_table = 'invoicing_invoices' verbose_name = _(u'invoice') verbose_name_plural = _(u'invoices') ordering = ('date_issue', 'number') def __unicode__(self): return self.full_number def save(self, **kwargs): if self.number in EMPTY_VALUES: self.number = self._get_next_number() if self.full_number in EMPTY_VALUES: self.full_number = self._get_full_number() return super(Invoice, self).save(**kwargs) def get_absolute_url(self): return reverse('invoicing:invoice_detail', args=(self.pk, )) def _get_next_number(self): """ Returnes next invoice number based on ``settings.INVOICING_COUNTER_PERIOD``. .. warning:: This is only used to prepopulate ``number`` field on saving new invoice. To get invoice number always use ``number`` field. .. note:: To get invoice full number use ``full_number`` field. :return: string (generated next number) """ invoice_counter_reset = getattr(settings, 'INVOICING_COUNTER_PERIOD', Invoice.COUNTER_PERIOD.YEARLY) if invoice_counter_reset == Invoice.COUNTER_PERIOD.DAILY: relative_invoices = Invoice.objects.filter( date_issue=self.date_issue, type=self.type) elif invoice_counter_reset == Invoice.COUNTER_PERIOD.YEARLY: relative_invoices = Invoice.objects.filter( date_issue__year=self.date_issue.year, type=self.type) elif invoice_counter_reset == Invoice.COUNTER_PERIOD.MONTHLY: relative_invoices = Invoice.objects.filter( date_issue__year=self.date_issue.year, date_issue__month=self.date_issue.month, type=self.type) else: raise ImproperlyConfigured( "INVOICING_COUNTER_PERIOD can be set only to these values: DAILY, MONTHLY, YEARLY." ) last_number = relative_invoices.aggregate( Max('number'))['number__max'] or 0 return last_number + 1 def _get_full_number(self): """ Generates on the fly invoice full number from template provided by ``settings.INVOICING_NUMBER_FORMAT``. ``Invoice`` object is provided as ``invoice`` variable to the template, therefore all object fields can be used to generate full number format. .. warning:: This is only used to prepopulate ``full_number`` field on saving new invoice. To get invoice full number always use ``full_number`` field. :return: string (generated full number) """ number_format = getattr( settings, "INVOICING_NUMBER_FORMAT", "{{ invoice.date_issue|date:'Y' }}/{{ invoice.number }}") return Template(number_format).render(Context({'invoice': self})) @property def taxation_policy(self): taxation_policy = getattr(settings, 'INVOICING_TAXATION_POLICY', None) if taxation_policy is not None: return import_name(taxation_policy) # Check if supplier is from EU if self.supplier_country: if EUTaxationPolicy.is_in_EU(self.supplier_country.code): return EUTaxationPolicy return None @property def is_overdue(self): return self.date_due < now().date() and self.status != self.STATUS.PAID @property def overdue_days(self): return (now().date() - self.date_due).days @property def payment_term(self): return (self.date_due - self.date_issue).days def set_supplier_data(self, supplier): self.supplier_name = supplier.get('name') self.supplier_street = supplier.get('street', None) self.supplier_zip = supplier.get('zip', None) self.supplier_city = supplier.get('city', None) self.supplier_country = supplier.get('country_code') self.supplier_registration_id = supplier.get('registration_id', None) self.supplier_tax_id = supplier.get('tax_id', None) self.supplier_vat_id = supplier.get('vat_id', None) self.supplier_additional_info = supplier.get('additional_info', None) bank = supplier.get('bank') self.bank_name = bank.get('name') self.bank_street = bank.get('street') self.bank_zip = bank.get('zip') self.bank_city = bank.get('city') self.bank_country = bank.get('country_code') self.bank_iban = bank.get('iban') self.bank_swift_bic = bank.get('swift_bic') def set_customer_data(self, customer): self.customer_name = customer.get('name') self.customer_street = customer.get('street', None) self.customer_zip = customer.get('zip', None) self.customer_city = customer.get('city', None) self.customer_country = customer.get('country_code') self.customer_registration_id = customer.get('registration_id', None) self.customer_tax_id = customer.get('tax_id', None) self.customer_vat_id = customer.get('vat_id', None) self.customer_additional_info = customer.get('additional_info', None) def set_shipping_data(self, shipping): self.shipping_name = shipping.get('name', None) self.shipping_street = shipping.get('street', None) self.shipping_zip = shipping.get('zip', None) self.shipping_city = shipping.get('city', None) self.shipping_country = shipping.get('country_code', None) def is_supplier_vat_id_visible(self): # VAT is not 0 if self.vat != 0 or self.invoiceitem_set.filter( tax_rate__gt=0).exists(): return True # VAT is 0, check if customer is from EU and from same country as supplier is_EU_customer = EUTaxationPolicy.is_in_EU( self.customer_country.code) if self.customer_country else False return is_EU_customer and self.supplier_country != self.customer_country @property def subtotal(self): sum = 0 for item in self.invoiceitem_set.all(): sum += item.subtotal return round_to_two_places(sum) @property def vat(self): vat = 0 for item in self.invoiceitem_set.all(): vat += item.vat return round_to_two_places(vat) @property def discount_value(self): total = self.subtotal + self.vat # subtotal with vat discount_value = total * (Decimal(self.discount) / 100 ) # subtract discount amount return round_to_two_places(discount_value) @property def total(self): total = self.subtotal + self.vat # subtotal with vat total *= ( (100 - Decimal(self.discount)) / 100) # subtract discount amount total -= self.credit # subtract credit #total -= self.already_paid # subtract already paid return round_to_two_places(total)
class Invoice(models.Model): client = models.ForeignKey(User, related_name='invoices', null=True, on_delete=models.SET_NULL) name = models.CharField(max_length=100, default='My invoice') YEARLY = 'Y' MONTHLY = 'M' WEEKLY = 'W' DAILY = 'D' REGULARITY_CHOICES = [ (YEARLY, 'Yearly'), (MONTHLY, 'Monthly'), (WEEKLY, 'Weekly'), (DAILY, 'Daily'), ] regularity = models.CharField(max_length=1, choices=REGULARITY_CHOICES, default=MONTHLY) is_regular = models.BooleanField() due_date = models.DateField() is_paid = models.BooleanField(default=False) WEBSITE = 'WEB' FILE = 'FIL' CREDENTIALS = 'CRD' INVOICE_TYPE_CHOICES = [ (WEBSITE, 'Website'), (FILE, 'File'), (CREDENTIALS, 'Credentials'), ] invoice_type = models.CharField(max_length=3, choices=INVOICE_TYPE_CHOICES) website = models.URLField(blank=True) login = models.CharField(max_length=100, blank=True) password = models.CharField(max_length=100, blank=True) file = models.FileField(upload_to='user_files', blank=True) iban = IBANField(blank=True) full_name = models.CharField(max_length=100, blank=True) total = models.DecimalField(decimal_places=2, blank=True, max_digits=9) comments = models.TextField() def get_regular_delta(self) -> relativedelta: if self.regularity == self.DAILY: return relativedelta(days=1) if self.regularity == self.WEEKLY: return relativedelta(weeks=1) if self.regularity == self.MONTHLY: return relativedelta(months=1) if self.regularity == self.YEARLY: return relativedelta(years=1) def pay(self, author: User): # todo validation self.client.add_to_balance(-self.total) if self.is_regular: self.due_date += self.get_regular_delta() else: self.is_paid = True self.invoice_logs.create(paid_by=author) self.save() def __str__(self): return f"Invoice {self.name} till {self.due_date}"
class Contact(MoneybirdSynchronizableResourceModel): class Meta: verbose_name = "contact" verbose_name_plural = "contacts" moneybird_resource_path_name = "contacts" moneybird_resource_name = "contact" moneybird_data_fields = [ "company_name", "firstname", "lastname", "attention", "address1", "address2", "zipcode", "city", "country", "phone", "delivery_method", "send_invoices_to_attention", "send_invoices_to_email", "send_estimates_to_attention", "send_estimates_to_email", "email", "email_ubl", "customer_id", "chamber_of_commerce", "tax_number", "bank_account", "sepa_active", "sepa_iban", "sepa_iban_account_name", "sepa_bic", "sepa_mandate_id", "sepa_sequence_type", ] moneybird_readonly_data_fields = ["attention", "email"] DELIVERY_METHOD_EMAIL = "Email" DELIVERY_METHOD_SIMPLERINVOICING = "Simplerinvoicing" DELIVERY_METHOD_POST = "Post" DELIVERY_METHOD_MANUAL = "Manual" DELIVERY_METHOD_CHOICES = ( (DELIVERY_METHOD_EMAIL, "Email"), (DELIVERY_METHOD_SIMPLERINVOICING, "Simplerinvoicing"), (DELIVERY_METHOD_POST, "Post"), (DELIVERY_METHOD_MANUAL, "Manual"), ) SEPA_RCUR = "RCUR" SEPA_FRST = "FRST" SEPA_OOFF = "OOFF" SEPA_FNAL = "FNAL" SEPA_SEQUENCE_TYPE_CHOICES = ( (SEPA_RCUR, "RCUR"), (SEPA_FRST, "FRST"), (SEPA_OOFF, "OOFF"), (SEPA_FNAL, "FNAL"), ) company_name = models.CharField(blank=True, null=True, max_length=100) firstname = models.CharField(blank=True, null=True, max_length=100) lastname = models.CharField(blank=True, null=True, max_length=100) address1 = models.CharField(blank=True, null=True, max_length=100) address2 = models.CharField(blank=True, null=True, max_length=100) zipcode = models.CharField(blank=True, null=True, max_length=10) city = models.CharField(blank=True, null=True, max_length=100) country = CountryField( blank=True, null=True, help_text="Will be automatically set to the standard Moneybird administration country if empty.", ) phone = models.CharField(blank=True, null=True, max_length=100) delivery_method = models.CharField( blank=True, null=True, choices=DELIVERY_METHOD_CHOICES, default=DELIVERY_METHOD_EMAIL, max_length=16 ) customer_id = models.IntegerField( blank=True, null=True, help_text="Will be assigned automatically if empty. Should be unique for the administration.", ) tax_number = models.CharField(blank=True, null=True, max_length=100) chamber_of_commerce = models.CharField(blank=True, null=True, max_length=100) bank_account = models.CharField(blank=True, null=True, max_length=100) attention = models.CharField(blank=True, null=True, max_length=100) email = models.CharField(blank=True, null=True, max_length=200,) email_ubl = models.BooleanField(blank=True, null=True) send_invoices_to_attention = models.CharField(blank=True, null=True, max_length=100) send_invoices_to_email = models.CharField( blank=True, null=True, max_length=200, help_text="Can be a single email or a comma-separated list of emails" ) send_estimates_to_attention = models.CharField(blank=True, null=True, max_length=100) send_estimates_to_email = models.CharField( blank=True, null=True, max_length=200, help_text="Can be a single email or a comma-separated list of emails" ) sepa_active = models.BooleanField(default=False, help_text="When true, all SEPA fields are required.") sepa_iban = IBANField(enforce_database_constraint=True, blank=True, null=True, max_length=100) sepa_iban_account_name = models.CharField(blank=True, null=True, max_length=100) sepa_bic = models.CharField(blank=True, null=True, max_length=100) sepa_mandate_id = models.CharField(blank=True, null=True, max_length=100) sepa_mandate_date = models.DateField(blank=True, null=True, help_text="Should be a date in the past.") sepa_sequence_type = models.CharField(blank=True, null=True, choices=SEPA_SEQUENCE_TYPE_CHOICES, max_length=4) # TODO add sales_invoices_url def get_moneybird_attr(self, field): if field == "country": return self.country.code return super(Contact, self).get_moneybird_attr(field) def set_moneybird_attr(self, field, value): if field == "country": self.country = value return super(Contact, self).set_moneybird_attr(field, value) def get_full_name(self): if self.firstname and self.lastname: return f"{self.firstname} {self.lastname}" elif self.firstname: return self.firstname return self.lastname def get_display_name(self): if self.company_name and self.get_full_name(): return f"{self.company_name} ({self.get_full_name()})" elif self.company_name: return self.company_name return self.get_full_name() def __str__(self): if self.customer_id: return f"{self.get_display_name()} ({self.customer_id})" return self.get_display_name()
class Account(models.Model): number = IBANField(primary_key=True) label = models.CharField(max_length=255) def __str__(self): return self.label