diff --git a/accounts/models.py b/accounts/models.py index fc8e78e..da618e1 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -244,16 +244,19 @@ def invoice_footer(canvas, doc): canvas.saveState() canvas.setFont('Times-Roman', 10) PAGE_WIDTH = defaultPageSize[0] - footer_text = "%s %s - SIRET : %s - %s, %s %s" % (user.first_name, - user.last_name, - user.get_profile().company_id, - user.get_profile().address.street.replace("\n", ", ").replace("\r", ""), - user.get_profile().address.zipcode, - user.get_profile().address.city) + footer_text = "%s %s - %s, %s %s" % (user.first_name, + user.last_name, + user.get_profile().address.street.replace("\n", ", ").replace("\r", ""), + user.get_profile().address.zipcode, + user.get_profile().address.city) if user.get_profile().address.country: footer_text = footer_text + u", %s" % (user.get_profile().address.country) canvas.drawCentredString(PAGE_WIDTH / 2.0, 0.5 * inch, footer_text) + extra_info = u"SIRET : %s" % (user.get_profile().company_id) + if user.get_profile().vat_number: + extra_info = u"%s - N° TVA : %s" % (extra_info, user.get_profile().vat_number) + canvas.drawCentredString(PAGE_WIDTH / 2.0, 0.35 * inch, extra_info) canvas.restoreState() filename = ugettext('invoice_%(invoice_id)d.pdf') % {'invoice_id': self.invoice_id} @@ -379,10 +382,14 @@ def invoice_footer(canvas, doc): story.append(spacer3) # invoice row list - data = [[ugettext('Label'), ugettext('Quantity'), ugettext('Unit price'), ugettext('Total')]] rows = self.invoice_rows.all() extra_rows = 0 - label_width = 4.5 * inch + data = [[ugettext('Label'), ugettext('Quantity'), ugettext('Unit price'), ugettext('Total excl tax')]] + if user.get_profile().vat_number: + data[0].append(ugettext('VAT')) + label_width = 4.0 * inch + else: + label_width = 4.5 * inch for row in rows: label = row.label if row.proposal.reference: @@ -397,10 +404,17 @@ def invoice_footer(canvas, doc): unit_price = unit_price.quantize(Decimal(1)) if unit_price == unit_price.to_integral() else unit_price.normalize() total = row.quantity * row.unit_price total = total.quantize(Decimal(1)) if total == total.to_integral() else total.normalize() - data.append([label, localize(quantity), "%s %s" % (localize(unit_price), "€".decode('utf-8')), "%s %s" % (localize(total), "€".decode('utf-8'))]) + + data_row = [label, localize(quantity), "%s %s" % (localize(unit_price), "€".decode('utf-8')), "%s %s" % (localize(total), "€".decode('utf-8'))] + if user.get_profile().vat_number: + data_row.append("%s%%" % (localize(row.vat_rate))) + data.append(data_row) for extra_row in splitted_para.lines[1:]: label = " ".join(extra_row[1]) - data.append([label, '', '', '']) + if user.get_profile().vat_number: + data.append([label, '', '', '', '']) + else: + data.append([label, '', '', '']) extra_rows = extra_rows + 1 row_count = len(rows) + extra_rows @@ -415,19 +429,29 @@ def invoice_footer(canvas, doc): max_row_count = max_row_count + normal_page_count for i in range(max_row_count - row_count): - data.append(['', '', '', '']) - - row_table = Table(data, [4.7 * inch, 0.8 * inch, 0.9 * inch, 0.8 * inch], (max_row_count + 1) * [0.3 * inch]) - row_table.setStyle(TableStyle([('ALIGN', (0, 0), (-1, 0), 'CENTER'), - ('ALIGN', (1, 0), (-1, -1), 'CENTER'), - ('FONT', (0, 0), (-1, 0), 'Times-Bold'), - ('BOX', (0, 0), (-1, 0), 0.25, colors.black), - ('INNERGRID', (0, 0), (-1, 0), 0.25, colors.black), - ('BOX', (0, 1), (0, -1), 0.25, colors.black), - ('BOX', (1, 1), (1, -1), 0.25, colors.black), - ('BOX', (2, 1), (2, -1), 0.25, colors.black), - ('BOX', (3, 1), (3, -1), 0.25, colors.black), - ])) + if user.get_profile().vat_number: + data.append(['', '', '', '', '']) + else: + data.append(['', '', '', '']) + + if user.get_profile().vat_number: + row_table = Table(data, [4.2 * inch, 0.8 * inch, 0.9 * inch, 0.8 * inch, 0.5 * inch], (max_row_count + 1) * [0.3 * inch]) + else: + row_table = Table(data, [4.7 * inch, 0.8 * inch, 0.9 * inch, 0.8 * inch], (max_row_count + 1) * [0.3 * inch]) + + row_style = [('ALIGN', (0, 0), (-1, 0), 'CENTER'), + ('ALIGN', (1, 0), (-1, -1), 'CENTER'), + ('FONT', (0, 0), (-1, 0), 'Times-Bold'), + ('BOX', (0, 0), (-1, 0), 0.25, colors.black), + ('INNERGRID', (0, 0), (-1, 0), 0.25, colors.black), + ('BOX', (0, 1), (0, -1), 0.25, colors.black), + ('BOX', (1, 1), (1, -1), 0.25, colors.black), + ('BOX', (2, 1), (2, -1), 0.25, colors.black), + ('BOX', (3, 1), (3, -1), 0.25, colors.black)] + if user.get_profile().vat_number: + row_style.append(('BOX', (4, 1), (4, -1), 0.25, colors.black)) + + row_table.setStyle(TableStyle(row_style)) story.append(row_table) @@ -445,11 +469,37 @@ def invoice_footer(canvas, doc): if self.owner.get_profile().bic: left_block.append(Paragraph(_("BIC/SWIFT : %s") % (self.owner.get_profile().bic), styleNSmall)) + if user.get_profile().vat_number: + right_block = [Paragraph(_("Total excl tax : %(amount)s %(currency)s") % {'amount': localize(invoice_amount), 'currency' : "€".decode('utf-8')}, styleN)] + vat_amounts = {} + for row in rows: + vat_rate = row.vat_rate + vat_amount = row.amount * vat_rate / 100 + if vat_rate in vat_amounts: + vat_amounts[vat_rate] = vat_amounts[vat_rate] + vat_amount + else: + vat_amounts[vat_rate] = vat_amount + for vat_rate, vat_amount in vat_amounts.items(): + vat_amount = round(vat_amount, 2) + #vat_amount = vat_amount.quantize(Decimal(1)) if vat_amount == vat_amount.to_integral() else vat_amount.normalize() + right_block.append(Paragraph(_("VAT %(vat_rate)s%% : %(vat_amount)s %(currency)s") % {'vat_rate': localize(vat_rate), + 'vat_amount': localize(vat_amount), + 'currency' : "€".decode('utf-8')}, + styleN)) + + incl_tax_amount = invoice_amount + sum(vat_amounts.values()) + #incl_tax_amount = incl_tax_amount.quantize(Decimal(1)) if incl_tax_amount == incl_tax_amount.to_integral() else incl_tax_amount.normalize() + incl_tax_amount = round(incl_tax_amount, 2) + right_block.append(Spacer(1, 0.25 * inch)) + right_block.append(Paragraph(_("TOTAL incl tax : %(amount)s %(currency)s") % {'amount': localize(incl_tax_amount), 'currency' : "€".decode('utf-8')}, styleTotal)) + else: + right_block = [Paragraph(_("TOTAL excl tax : %(amount)s %(currency)s") % {'amount': localize(invoice_amount), 'currency' : "€".decode('utf-8')}, styleTotal), + Spacer(1, 0.25 * inch), + Paragraph(u"TVA non applicable, art. 293 B du CGI", styleN)] + data = [[left_block, '', - [Paragraph(_("TOTAL excl. VAT : %(amount)s %(currency)s") % {'amount': localize(invoice_amount), 'currency' : "€".decode('utf-8')}, styleTotal), - Spacer(1, 0.25 * inch), - Paragraph(u"TVA non applicable, art. 293 B du CGI", styleN)]], ] + right_block], ] if self.execution_begin_date and self.execution_end_date: data[0][0].insert(1, Paragraph(_("Execution dates : %(begin_date)s to %(end_date)s") % {'begin_date': localize(self.execution_begin_date), 'end_date' : localize(self.execution_end_date)}, styleN)) diff --git a/accounts/tests.py b/accounts/tests.py index f9d0e67..b8e93ef 100644 --- a/accounts/tests.py +++ b/accounts/tests.py @@ -13,8 +13,9 @@ ProposalRow from accounts.models import INVOICE_STATE_EDITED, Invoice, InvoiceRow, \ INVOICE_STATE_SENT, InvoiceRowAmountError, PAYMENT_TYPE_CHECK, \ - PAYMENT_TYPE_CASH, Expense, INVOICE_STATE_PAID + PAYMENT_TYPE_CASH, Expense, INVOICE_STATE_PAID, VAT_RATES_19_6 from contact.models import Country +from autoentrepreneur.models import UserProfile class ExpensePermissionTest(TestCase): fixtures = ['test_users'] @@ -798,7 +799,7 @@ def testDownloadPdf(self): content = response.content.split("\n") invariant_content = content[0:66] + content[67:110] + content[111:-1] self.assertEquals(hashlib.md5("\n".join(invariant_content)).hexdigest(), - "15afee56ba684f4b97e334c386559b86") + "119afaaace168edcf2ddcc82297b4bfe") def testInvoiceBookDownloadPdf(self): """ @@ -1344,6 +1345,93 @@ def testBug207(self): response = self.client.get(reverse('invoice_download', kwargs={'id': i.id})) self.assertEqual(response.status_code, 200) + def testVat(self): + profile = UserProfile.objects.get(user=1) + profile.vat_number = 'FR010123456789123' + profile.save() + + i = Invoice.objects.create(customer_id=self.proposal.project.customer_id, + invoice_id=1, + state=INVOICE_STATE_EDITED, + amount='2', + edition_date=datetime.date(2010, 8, 31), + payment_date=datetime.date(2010, 9, 30), + paid_date=None, + payment_type=PAYMENT_TYPE_CHECK, + execution_begin_date=datetime.date(2010, 8, 1), + execution_end_date=datetime.date(2010, 8, 7), + penalty_date=datetime.date(2010, 10, 8), + penalty_rate='1.5', + discount_conditions='Nothing', + owner_id=1) + + i_row = InvoiceRow.objects.create(proposal_id=self.proposal.id, + invoice_id=i.id, + label='Day of work', + category=ROW_CATEGORY_SERVICE, + quantity=1, + unit_price='1', + balance_payments=False, + vat_rate=VAT_RATES_19_6, + owner_id=1) + i_row = InvoiceRow.objects.create(proposal_id=self.proposal.id, + invoice_id=i.id, + label='Day of work', + category=ROW_CATEGORY_SERVICE, + quantity=1, + unit_price='1', + balance_payments=False, + vat_rate=VAT_RATES_19_6, + owner_id=1) + response = self.client.get(reverse('invoice_detail', kwargs={'id': i.id})) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['invoice'].get_vat(), Decimal('0.392')) + self.assertEqual(response.context['invoice'].amount_including_tax(), Decimal('2.392')) + + def testDownloadPdfWithVat(self): + """ + Tests non-regression on pdf + """ + profile = User.objects.get(pk=1).get_profile() + profile.iban_bban = 'FR76 1234 1234 1234 1234 1234 123' + profile.bic = 'CCBPFRABCDE' + profile.vat_number = 'FR010123456789123' + profile.save() + i = Invoice.objects.create(customer_id=self.proposal.project.customer_id, + invoice_id=1, + state=INVOICE_STATE_EDITED, + amount='1000', + edition_date=datetime.date(2010, 8, 31), + payment_date=datetime.date(2010, 9, 30), + paid_date=None, + payment_type=PAYMENT_TYPE_CHECK, + execution_begin_date=datetime.date(2010, 8, 1), + execution_end_date=datetime.date(2010, 8, 7), + penalty_date=datetime.date(2010, 10, 8), + penalty_rate='1.5', + discount_conditions='Nothing', + owner_id=1) + + i_row = InvoiceRow.objects.create(proposal_id=self.proposal.id, + invoice_id=i.id, + label='Day of work', + category=ROW_CATEGORY_SERVICE, + quantity=10, + unit_price='100', + balance_payments=False, + vat_rate=VAT_RATES_19_6, + owner_id=1) + + response = self.client.get(reverse('invoice_download', kwargs={'id': i.id})) + self.assertEqual(response.status_code, 200) + f = open('/tmp/invoiceVat.pdf', 'w') + f.write(response.content) + f.close() + content = response.content.split("\n") + invariant_content = content[0:66] + content[67:110] + content[111:-1] + self.assertEquals(hashlib.md5("\n".join(invariant_content)).hexdigest(), + "142188046b08a8c9f39868815fa7bd11") + class InvoiceBug106Test(TransactionTestCase): fixtures = ['test_users', 'test_contacts', 'test_projects'] diff --git a/autoentrepreneur/migrations/0021_auto__del_field_userprofile_vat_enabled__add_field_userprofile_vat_num.py b/autoentrepreneur/migrations/0021_auto__del_field_userprofile_vat_enabled__add_field_userprofile_vat_num.py new file mode 100644 index 0000000..f83313f --- /dev/null +++ b/autoentrepreneur/migrations/0021_auto__del_field_userprofile_vat_enabled__add_field_userprofile_vat_num.py @@ -0,0 +1,125 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Deleting field 'UserProfile.vat_enabled' + db.delete_column('autoentrepreneur_userprofile', 'vat_enabled') + + # Adding field 'UserProfile.vat_number' + db.add_column('autoentrepreneur_userprofile', 'vat_number', self.gf('django.db.models.fields.CharField')(default='', max_length=20), keep_default=False) + + + def backwards(self, orm): + + # Adding field 'UserProfile.vat_enabled' + db.add_column('autoentrepreneur_userprofile', 'vat_enabled', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False) + + # Deleting field 'UserProfile.vat_number' + db.delete_column('autoentrepreneur_userprofile', 'vat_number') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'autoentrepreneur.saleslimit': { + 'Meta': {'object_name': 'SalesLimit'}, + 'activity': ('django.db.models.fields.IntegerField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'limit': ('django.db.models.fields.IntegerField', [], {}), + 'limit2': ('django.db.models.fields.IntegerField', [], {}), + 'year': ('django.db.models.fields.IntegerField', [], {}) + }, + 'autoentrepreneur.subscription': { + 'Meta': {'object_name': 'Subscription', '_ormbases': ['core.OwnedObject']}, + 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '150', 'null': 'True', 'blank': 'True'}), + 'expiration_date': ('django.db.models.fields.DateField', [], {'db_index': 'True'}), + 'ownedobject_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['core.OwnedObject']", 'unique': 'True', 'primary_key': 'True'}), + 'state': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'transaction_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'}) + }, + 'autoentrepreneur.userprofile': { + 'Meta': {'object_name': 'UserProfile'}, + 'activity': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'address': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contact.Address']"}), + 'bic': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '11', 'blank': 'True'}), + 'company_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}), + 'company_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'creation_help': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'freeing_tax_payment': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'iban_bban': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '34', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'payment_option': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'phonenumber': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}), + 'professional_category': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'professional_email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '75', 'blank': 'True'}), + 'unregister_datetime': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "''", 'unique': 'True', 'max_length': '36'}), + 'vat_number': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}) + }, + 'contact.address': { + 'Meta': {'object_name': 'Address', '_ormbases': ['core.OwnedObject']}, + 'city': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}), + 'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contact.Country']", 'null': 'True', 'blank': 'True'}), + 'ownedobject_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['core.OwnedObject']", 'unique': 'True', 'primary_key': 'True'}), + 'street': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'zipcode': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}) + }, + 'contact.country': { + 'Meta': {'ordering': "['country_name']", 'object_name': 'Country'}, + 'country_code2': ('django.db.models.fields.CharField', [], {'max_length': '2'}), + 'country_code3': ('django.db.models.fields.CharField', [], {'max_length': '3'}), + 'country_name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'core.ownedobject': { + 'Meta': {'object_name': 'OwnedObject'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "''", 'unique': 'True', 'max_length': '36'}) + } + } + + complete_apps = ['autoentrepreneur'] diff --git a/autoentrepreneur/migrations/0022_auto__chg_field_userprofile_vat_number.py b/autoentrepreneur/migrations/0022_auto__chg_field_userprofile_vat_number.py new file mode 100644 index 0000000..7c8a60e --- /dev/null +++ b/autoentrepreneur/migrations/0022_auto__chg_field_userprofile_vat_number.py @@ -0,0 +1,119 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'UserProfile.vat_number' + db.alter_column('autoentrepreneur_userprofile', 'vat_number', self.gf('django.db.models.fields.CharField')(max_length=20, null=True)) + + + def backwards(self, orm): + + # Changing field 'UserProfile.vat_number' + db.alter_column('autoentrepreneur_userprofile', 'vat_number', self.gf('django.db.models.fields.CharField')(max_length=20)) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'autoentrepreneur.saleslimit': { + 'Meta': {'object_name': 'SalesLimit'}, + 'activity': ('django.db.models.fields.IntegerField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'limit': ('django.db.models.fields.IntegerField', [], {}), + 'limit2': ('django.db.models.fields.IntegerField', [], {}), + 'year': ('django.db.models.fields.IntegerField', [], {}) + }, + 'autoentrepreneur.subscription': { + 'Meta': {'object_name': 'Subscription', '_ormbases': ['core.OwnedObject']}, + 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '150', 'null': 'True', 'blank': 'True'}), + 'expiration_date': ('django.db.models.fields.DateField', [], {'db_index': 'True'}), + 'ownedobject_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['core.OwnedObject']", 'unique': 'True', 'primary_key': 'True'}), + 'state': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'transaction_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'}) + }, + 'autoentrepreneur.userprofile': { + 'Meta': {'object_name': 'UserProfile'}, + 'activity': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'address': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contact.Address']"}), + 'bic': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '11', 'blank': 'True'}), + 'company_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}), + 'company_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'creation_help': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'freeing_tax_payment': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'iban_bban': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '34', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'payment_option': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'phonenumber': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}), + 'professional_category': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'professional_email': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '75', 'blank': 'True'}), + 'unregister_datetime': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "''", 'unique': 'True', 'max_length': '36'}), + 'vat_number': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'null': 'True', 'blank': 'True'}) + }, + 'contact.address': { + 'Meta': {'object_name': 'Address', '_ormbases': ['core.OwnedObject']}, + 'city': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}), + 'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contact.Country']", 'null': 'True', 'blank': 'True'}), + 'ownedobject_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['core.OwnedObject']", 'unique': 'True', 'primary_key': 'True'}), + 'street': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'zipcode': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '10', 'blank': 'True'}) + }, + 'contact.country': { + 'Meta': {'ordering': "['country_name']", 'object_name': 'Country'}, + 'country_code2': ('django.db.models.fields.CharField', [], {'max_length': '2'}), + 'country_code3': ('django.db.models.fields.CharField', [], {'max_length': '3'}), + 'country_name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'core.ownedobject': { + 'Meta': {'object_name': 'OwnedObject'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "''", 'unique': 'True', 'max_length': '36'}) + } + } + + complete_apps = ['autoentrepreneur'] diff --git a/autoentrepreneur/models.py b/autoentrepreneur/models.py index ce1afe1..f0be266 100644 --- a/autoentrepreneur/models.py +++ b/autoentrepreneur/models.py @@ -169,7 +169,7 @@ class UserProfile(models.Model): bic = models.CharField(max_length=11, blank=True, default='', verbose_name=_('BIC/SWIFT'), help_text=_('will appear on your invoices if set')) logo_file = models.FileField(upload_to=logo_upload_to_handler, null=True, blank=True, storage=store, verbose_name=_('Custom header'), help_text=_('will appear in place of your personnal informations on proposals and invoices. Maximum width and height: 252x137')) uuid = models.CharField(max_length=36, unique=True, default=uuid.uuid4) - vat_enabled = models.BooleanField(default=False, verbose_name=_('Enable VAT'), help_text=_('If you overrun the second sales limit, you need to declare VAT')) + vat_number = models.CharField(max_length=20, default='', null=True, blank=True, verbose_name=_('VAT number'), help_text=_('If you overrun the second sales limit, you need to declare VAT. Filling this field enables VAT management on invoices')) def __unicode__(self): return self.user.__unicode__() diff --git a/core/views.py b/core/views.py index 6beeb03..32a665e 100644 --- a/core/views.py +++ b/core/views.py @@ -1,4 +1,5 @@ import logging +from decimal import Decimal from django.shortcuts import render_to_response, redirect, get_object_or_404 from django.template.context import RequestContext, Context from django.utils.translation import ugettext_lazy as _, ugettext @@ -12,7 +13,7 @@ from django.contrib.auth.decorators import login_required from django.utils import simplejson from accounts.models import Expense, Invoice, INVOICE_STATE_PAID, \ - PAYMENT_TYPE_BANK_CARD, InvoiceRow + PAYMENT_TYPE_BANK_CARD, InvoiceRow, VAT_RATES_19_6 from core.decorators import settings_required, disabled_for_demo from autoentrepreneur.models import AUTOENTREPRENEUR_ACTIVITY_PRODUCT_SALE_BIC, \ Subscription, SUBSCRIPTION_STATE_NOT_PAID, SUBSCRIPTION_STATE_PAID, \ @@ -491,6 +492,9 @@ def paypal_ipn(request): # create an invoice for this payment # first, get the provider user provider = User.objects.get(email=settings.SERVICE_PROVIDER_EMAIL) + if provider.get_profile().vat_number: + payment_amount = Decimal(payment_amount) / Decimal('1.196') + # look for a customer corresponding to user address, created = Address.objects.get_or_create(contact__email=user.email, owner=provider, @@ -533,11 +537,16 @@ def paypal_ipn(request): update_date=datetime.date.today(), expiration_date=None, owner=provider) + + unit_price = Decimal(settings.PAYPAL_APP_SUBSCRIPTION_AMOUNT) + if provider.get_profile().vat_number: + unit_price = Decimal(unit_price) / Decimal('1.196') + proposal_row = ProposalRow.objects.create(proposal=proposal, label=item_name, category=ROW_CATEGORY_SERVICE, quantity=1, - unit_price='%s' % settings.PAYPAL_APP_SUBSCRIPTION_AMOUNT, + unit_price='%s' % unit_price, owner=provider) # finally create invoice @@ -563,6 +572,7 @@ def paypal_ipn(request): quantity=1, unit_price=payment_amount, balance_payments=True, + vat_rate=VAT_RATES_19_6, owner=provider) # create expense for paypal fee expense = Expense.objects.create(date=datetime.date.today(), diff --git a/locale/en/LC_MESSAGES/django.mo b/locale/en/LC_MESSAGES/django.mo index ec766e4..b686dd1 100644 Binary files a/locale/en/LC_MESSAGES/django.mo and b/locale/en/LC_MESSAGES/django.mo differ diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 23f9209..6653c63 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-05-08 16:25+0200\n" +"POT-Creation-Date: 2011-05-10 22:58+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -25,16 +25,15 @@ msgstr "" msgid "French" msgstr "" -#: accounts/forms.py:8 accounts/models.py:38 accounts/models.py:165 +#: accounts/forms.py:8 accounts/models.py:39 accounts/models.py:166 #: accounts/views.py:178 accounts/views.py:252 backup/views.py:134 #: project/models.py:175 project/models.py:548 templates/expense/list.html:220 -#: templates/invoice/detail.html:25 templates/invoice/list.html:28 -#: templates/project/detail.html:32 templates/project/detail.html.py:60 -#: templates/proposal/detail.html:97 +#: templates/invoice/list.html:28 templates/project/detail.html:32 +#: templates/project/detail.html.py:60 templates/proposal/detail.html:97 msgid "Amount" msgstr "" -#: accounts/forms.py:20 accounts/models.py:163 templates/invoice/list.html:20 +#: accounts/forms.py:20 accounts/models.py:164 templates/invoice/list.html:20 msgid "Invoice id" msgstr "" @@ -43,7 +42,7 @@ msgstr "" msgid "An integer less than or equal to %d. Must be sequential." msgstr "" -#: accounts/forms.py:24 accounts/models.py:173 backup/views.py:137 +#: accounts/forms.py:24 accounts/models.py:174 backup/views.py:137 msgid "Penalty rate" msgstr "" @@ -59,89 +58,89 @@ msgstr "" msgid "This field is required since invoice state is set to \"paid\"." msgstr "" -#: accounts/forms.py:65 accounts/models.py:370 project/forms.py:63 +#: accounts/forms.py:65 accounts/models.py:387 project/forms.py:63 #: project/models.py:390 project/models.py:546 -#: templates/invoice/detail.html:72 templates/proposal/detail.html:66 +#: templates/invoice/detail.html:84 templates/proposal/detail.html:66 #: templates/proposal/detail_ajax.html:33 msgid "Quantity" msgstr "" -#: accounts/forms.py:66 accounts/models.py:370 project/forms.py:64 +#: accounts/forms.py:66 accounts/models.py:387 project/forms.py:64 #: project/models.py:390 project/models.py:547 -#: templates/invoice/detail.html:73 templates/proposal/detail.html:67 +#: templates/invoice/detail.html:85 templates/proposal/detail.html:67 #: templates/proposal/detail_ajax.html:34 msgid "Unit price" msgstr "" -#: accounts/models.py:29 +#: accounts/models.py:30 msgid "Cash" msgstr "" -#: accounts/models.py:30 +#: accounts/models.py:31 msgid "Bank card" msgstr "" -#: accounts/models.py:31 +#: accounts/models.py:32 msgid "Transfer" msgstr "" -#: accounts/models.py:32 +#: accounts/models.py:33 msgid "Check" msgstr "" -#: accounts/models.py:35 accounts/views.py:178 accounts/views.py:252 +#: accounts/models.py:36 accounts/views.py:178 accounts/views.py:252 #: templates/expense/list.html:217 msgid "Date" msgstr "" -#: accounts/models.py:35 accounts/models.py:166 accounts/models.py:167 -#: accounts/models.py:169 accounts/models.py:170 accounts/models.py:171 -#: accounts/models.py:172 autoentrepreneur/forms.py:13 +#: accounts/models.py:36 accounts/models.py:167 accounts/models.py:168 +#: accounts/models.py:170 accounts/models.py:171 accounts/models.py:172 +#: accounts/models.py:173 autoentrepreneur/forms.py:13 #: autoentrepreneur/models.py:108 autoentrepreneur/models.py:163 #: project/models.py:36 project/models.py:176 project/models.py:177 #: project/models.py:179 project/models.py:180 msgid "format: mm/dd/yyyy" msgstr "" -#: accounts/models.py:36 backup/views.py:134 project/models.py:173 +#: accounts/models.py:37 backup/views.py:134 project/models.py:173 #: templates/expense/list.html:218 templates/project/detail.html:29 msgid "Reference" msgstr "" -#: accounts/models.py:37 accounts/views.py:178 templates/expense/list.html:219 +#: accounts/models.py:38 accounts/views.py:178 templates/expense/list.html:219 msgid "Supplier" msgstr "" -#: accounts/models.py:39 accounts/models.py:168 accounts/views.py:178 +#: accounts/models.py:40 accounts/models.py:169 accounts/views.py:178 #: accounts/views.py:252 backup/views.py:135 templates/expense/list.html:221 #: templates/invoice/list.html:26 msgid "Payment type" msgstr "" -#: accounts/models.py:40 templates/expense/list.html:222 +#: accounts/models.py:41 templates/expense/list.html:222 msgid "Description" msgstr "" -#: accounts/models.py:56 +#: accounts/models.py:57 msgid "Edited" msgstr "" -#: accounts/models.py:57 newsletter/models.py:17 project/models.py:131 +#: accounts/models.py:58 newsletter/models.py:17 project/models.py:131 #: templates/newsletter/email_users.html:25 msgid "Sent" msgstr "" -#: accounts/models.py:58 autoentrepreneur/models.py:43 core/views.py:488 +#: accounts/models.py:59 autoentrepreneur/models.py:43 core/views.py:488 msgid "Paid" msgstr "" -#: accounts/models.py:162 accounts/views.py:252 backup/views.py:134 +#: accounts/models.py:163 accounts/views.py:252 backup/views.py:134 #: project/forms.py:28 project/models.py:32 project/models.py:111 #: templates/invoice/list.html:23 templates/project/list.html:20 msgid "Customer" msgstr "" -#: accounts/models.py:164 autoentrepreneur/models.py:107 backup/views.py:134 +#: accounts/models.py:165 autoentrepreneur/models.py:107 backup/views.py:134 #: bugtracker/models.py:35 project/forms.py:29 project/models.py:112 #: project/models.py:174 templates/backup/index.html:24 #: templates/backup/index.html.py:73 templates/core/subscribe.html:9 @@ -151,133 +150,156 @@ msgstr "" msgid "State" msgstr "" -#: accounts/models.py:166 backup/views.py:135 templates/invoice/list.html:21 +#: accounts/models.py:167 backup/views.py:135 templates/invoice/list.html:21 msgid "Edition date" msgstr "" -#: accounts/models.py:167 backup/views.py:135 +#: accounts/models.py:168 backup/views.py:135 msgid "Payment date" msgstr "" -#: accounts/models.py:169 backup/views.py:136 templates/invoice/list.html:22 +#: accounts/models.py:170 backup/views.py:136 templates/invoice/list.html:22 msgid "Paid date" msgstr "" -#: accounts/models.py:170 backup/views.py:136 +#: accounts/models.py:171 backup/views.py:136 msgid "Execution begin date" msgstr "" -#: accounts/models.py:171 backup/views.py:136 +#: accounts/models.py:172 backup/views.py:136 msgid "Execution end date" msgstr "" -#: accounts/models.py:172 backup/views.py:137 +#: accounts/models.py:173 backup/views.py:137 msgid "Penalty date" msgstr "" -#: accounts/models.py:174 backup/views.py:137 +#: accounts/models.py:175 backup/views.py:137 msgid "Discount conditions" msgstr "" -#: accounts/models.py:182 +#: accounts/models.py:183 #, python-format msgid "invoice #%d" msgstr "" -#: accounts/models.py:214 +#: accounts/models.py:215 #, python-format msgid "Invoice id must be less than or equal to %d" msgstr "" -#: accounts/models.py:216 accounts/views.py:351 +#: accounts/models.py:217 accounts/views.py:351 msgid "Invoice id must be unique" msgstr "" -#: accounts/models.py:226 accounts/views.py:348 +#: accounts/models.py:227 accounts/views.py:348 msgid "Amounts invoiced can't be greater than proposals remaining amounts" msgstr "" -#: accounts/models.py:247 +#: accounts/models.py:262 #, python-format msgid "invoice_%(invoice_id)d.pdf" msgstr "" -#: accounts/models.py:250 +#: accounts/models.py:265 #, python-format msgid "Invoice #%(invoice_id)d" msgstr "" -#: accounts/models.py:354 project/models.py:374 +#: accounts/models.py:369 project/models.py:374 #, python-format msgid "Date : %s" msgstr "" -#: accounts/models.py:364 +#: accounts/models.py:379 #, python-format msgid "INVOICE #%d" msgstr "" -#: accounts/models.py:370 faq/models.py:5 project/models.py:390 -#: project/models.py:544 templates/invoice/detail.html:68 +#: accounts/models.py:387 faq/models.py:5 project/models.py:390 +#: project/models.py:544 templates/invoice/detail.html:80 #: templates/proposal/detail.html:64 templates/proposal/detail_ajax.html:31 msgid "Label" msgstr "" -#: accounts/models.py:370 project/models.py:390 -#: templates/core/sales_widget.html:38 -msgid "Total" +#: accounts/models.py:387 +msgid "Total excl tax" +msgstr "" + +#: accounts/models.py:389 templates/invoice/detail.html:31 +msgid "VAT" msgstr "" -#: accounts/models.py:426 +#: accounts/models.py:462 #, python-format msgid "Payment date : %s" msgstr "" -#: accounts/models.py:427 +#: accounts/models.py:463 #, python-format msgid "Penalty begins on : %s" msgstr "" -#: accounts/models.py:428 +#: accounts/models.py:464 #, python-format msgid "Penalty rate : %s" msgstr "" -#: accounts/models.py:429 +#: accounts/models.py:465 #, python-format msgid "Discount conditions : %s" msgstr "" -#: accounts/models.py:432 +#: accounts/models.py:468 #, python-format msgid "IBAN/BBAN : %s" msgstr "" -#: accounts/models.py:434 +#: accounts/models.py:470 #, python-format msgid "BIC/SWIFT : %s" msgstr "" -#: accounts/models.py:438 project/models.py:447 +#: accounts/models.py:473 #, python-format -msgid "TOTAL excl. VAT : %(amount)s %(currency)s" +msgid "Total excl tax : %(amount)s %(currency)s" +msgstr "" + +#: accounts/models.py:485 +#, python-format +msgid "VAT %(vat_rate)s%% : %(vat_amount)s %(currency)s" msgstr "" -#: accounts/models.py:443 project/models.py:452 +#: accounts/models.py:494 +#, python-format +msgid "TOTAL incl tax : %(amount)s %(currency)s" +msgstr "" + +#: accounts/models.py:496 +#, python-format +msgid "TOTAL excl tax : %(amount)s %(currency)s" +msgstr "" + +#: accounts/models.py:505 project/models.py:452 #, python-format msgid "Execution dates : %(begin_date)s to %(end_date)s" msgstr "" -#: accounts/models.py:459 templates/invoice/detail.html:69 +#: accounts/models.py:522 accounts/models.py:523 accounts/models.py:524 +#, python-format +msgid "%s%%" +msgstr "" + +#: accounts/models.py:528 templates/invoice/detail.html:81 #: templates/invoice/edit.html:19 templates/invoice/edit.html.py:84 msgid "Proposal" msgstr "" -#: accounts/models.py:460 +#: accounts/models.py:529 msgid "Balance payments for the proposal" msgstr "" -#: accounts/models.py:460 +#: accounts/models.py:529 msgid "" "\"Balancing payments for the proposal\" means there will be no future " "invoices for the selected proposal. Thus the amount remaining to invoice for " @@ -285,6 +307,10 @@ msgid "" "when all invoices are paid." msgstr "" +#: accounts/models.py:530 +msgid "Vat" +msgstr "" + #: accounts/views.py:61 templates/core/charts_widget.html:37 msgid "Expenses" msgstr "" @@ -555,12 +581,22 @@ msgid "" "invoices. Maximum width and height: 252x137" msgstr "" -#: autoentrepreneur/models.py:185 autoentrepreneur/tests.py:716 +#: autoentrepreneur/models.py:172 +msgid "VAT number" +msgstr "" + +#: autoentrepreneur/models.py:172 +msgid "" +"If you overrun the second sales limit, you need to declare VAT. Filling this " +"field enables VAT management on invoices" +msgstr "" + +#: autoentrepreneur/models.py:186 autoentrepreneur/tests.py:716 #, python-format msgid "You've just unregistered from %(site)s" msgstr "" -#: autoentrepreneur/models.py:186 +#: autoentrepreneur/models.py:187 #, python-format msgid "" "You have left the site and your data will been deleted in " @@ -571,7 +607,7 @@ msgid "" "today, please come back to test later." msgstr "" -#: autoentrepreneur/models.py:192 +#: autoentrepreneur/models.py:193 #: templates/registration/activation_email.txt:3 #: templates/registration/password_reset_email.html:13 #, python-format @@ -725,7 +761,7 @@ msgid "User" msgstr "" #: bugtracker/models.py:31 faq/models.py:17 project/models.py:545 -#: templates/invoice/detail.html:71 templates/issue/list.html:21 +#: templates/invoice/detail.html:83 templates/issue/list.html:21 #: templates/message/list.html:20 templates/proposal/detail.html:65 #: templates/proposal/detail_ajax.html:32 msgid "Category" @@ -1619,6 +1655,10 @@ msgstr "" msgid "PROPOSAL %s" msgstr "" +#: project/models.py:390 templates/core/sales_widget.html:38 +msgid "Total" +msgstr "" + #: project/models.py:444 #, python-format msgid "Proposal valid through : %s" @@ -1629,6 +1669,11 @@ msgstr "" msgid "Payment delay : %s" msgstr "" +#: project/models.py:447 +#, python-format +msgid "TOTAL excl. VAT : %(amount)s %(currency)s" +msgstr "" + #: project/models.py:465 project/models.py:493 msgid "reference" msgstr "" @@ -1807,7 +1852,7 @@ msgstr "" #: templates/menu.html:29 templates/menu.html.py:65 #: templates/contact/edit.html:152 templates/expense/list.html:235 -#: templates/invoice/edit.html:157 templates/proposal/edit.html:217 +#: templates/invoice/edit.html:158 templates/proposal/edit.html:217 msgid "Add" msgstr "" @@ -1950,7 +1995,7 @@ msgid "No master aggreements" msgstr "" #: templates/contact/edit.html:179 templates/contract/edit.html:78 -#: templates/core/settings_edit.html:102 templates/invoice/edit.html:163 +#: templates/core/settings_edit.html:105 templates/invoice/edit.html:164 #: templates/newsletter/email_users.html:13 templates/project/edit.html:23 #: templates/proposal/edit.html:257 msgid "Save" @@ -1976,7 +2021,7 @@ msgstr "" msgid "No contacts" msgstr "" -#: templates/contract/detail.html:28 templates/invoice/detail.html:58 +#: templates/contract/detail.html:28 templates/invoice/detail.html:70 #: templates/proposal/detail.html:54 templates/proposal/detail.html.py:124 msgid "download pdf file" msgstr "" @@ -2282,33 +2327,45 @@ msgstr "" msgid "Invoice for %(customer)s" msgstr "" -#: templates/invoice/detail.html:58 templates/proposal/detail.html:54 +#: templates/invoice/detail.html:25 +msgid "Amount excluding tax" +msgstr "" + +#: templates/invoice/detail.html:36 +msgid "Amount including tax" +msgstr "" + +#: templates/invoice/detail.html:70 templates/proposal/detail.html:54 msgid "Exported file" msgstr "" -#: templates/invoice/detail.html:62 templates/invoice/edit.html:143 +#: templates/invoice/detail.html:74 templates/invoice/edit.html:143 #: templates/proposal/detail.html:58 templates/proposal/edit.html:204 msgid "Detail rows" msgstr "" -#: templates/invoice/detail.html:70 +#: templates/invoice/detail.html:82 msgid "Balance payments" msgstr "" -#: templates/invoice/detail.html:74 templates/proposal/detail.html:68 +#: templates/invoice/detail.html:86 templates/proposal/detail.html:68 #: templates/proposal/detail_ajax.html:35 msgid "Total price" msgstr "" -#: templates/invoice/detail.html:82 templates/newsletter/email_users.html:35 +#: templates/invoice/detail.html:88 +msgid "VAT rate" +msgstr "" + +#: templates/invoice/detail.html:97 templates/newsletter/email_users.html:35 msgid "yes" msgstr "" -#: templates/invoice/detail.html:82 templates/newsletter/email_users.html:35 +#: templates/invoice/detail.html:97 templates/newsletter/email_users.html:35 msgid "no" msgstr "" -#: templates/invoice/detail.html:93 templates/proposal/detail.html:85 +#: templates/invoice/detail.html:111 templates/proposal/detail.html:85 #: templates/proposal/detail_ajax.html:52 msgid "No rows" msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.mo b/locale/fr/LC_MESSAGES/django.mo index ec7d080..db48c82 100644 Binary files a/locale/fr/LC_MESSAGES/django.mo and b/locale/fr/LC_MESSAGES/django.mo differ diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 5a75efe..f95aedc 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-05-08 16:25+0200\n" +"POT-Creation-Date: 2011-05-10 22:58+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -26,16 +26,15 @@ msgstr "Anglais" msgid "French" msgstr "Français" -#: accounts/forms.py:8 accounts/models.py:38 accounts/models.py:165 +#: accounts/forms.py:8 accounts/models.py:39 accounts/models.py:166 #: accounts/views.py:178 accounts/views.py:252 backup/views.py:134 #: project/models.py:175 project/models.py:548 templates/expense/list.html:220 -#: templates/invoice/detail.html:25 templates/invoice/list.html:28 -#: templates/project/detail.html:32 templates/project/detail.html.py:60 -#: templates/proposal/detail.html:97 +#: templates/invoice/list.html:28 templates/project/detail.html:32 +#: templates/project/detail.html.py:60 templates/proposal/detail.html:97 msgid "Amount" msgstr "Montant" -#: accounts/forms.py:20 accounts/models.py:163 templates/invoice/list.html:20 +#: accounts/forms.py:20 accounts/models.py:164 templates/invoice/list.html:20 msgid "Invoice id" msgstr "Numéro de facture" @@ -44,7 +43,7 @@ msgstr "Numéro de facture" msgid "An integer less than or equal to %d. Must be sequential." msgstr "Un entier inférieur ou égal à %d. Doit être séquentiel." -#: accounts/forms.py:24 accounts/models.py:173 backup/views.py:137 +#: accounts/forms.py:24 accounts/models.py:174 backup/views.py:137 msgid "Penalty rate" msgstr "Taux de pénalité" @@ -64,89 +63,89 @@ msgstr "" msgid "This field is required since invoice state is set to \"paid\"." msgstr "Ce champ est obligatoire car la facture est à l'état \"payée\"." -#: accounts/forms.py:65 accounts/models.py:370 project/forms.py:63 +#: accounts/forms.py:65 accounts/models.py:387 project/forms.py:63 #: project/models.py:390 project/models.py:546 -#: templates/invoice/detail.html:72 templates/proposal/detail.html:66 +#: templates/invoice/detail.html:84 templates/proposal/detail.html:66 #: templates/proposal/detail_ajax.html:33 msgid "Quantity" msgstr "Quantité" -#: accounts/forms.py:66 accounts/models.py:370 project/forms.py:64 +#: accounts/forms.py:66 accounts/models.py:387 project/forms.py:64 #: project/models.py:390 project/models.py:547 -#: templates/invoice/detail.html:73 templates/proposal/detail.html:67 +#: templates/invoice/detail.html:85 templates/proposal/detail.html:67 #: templates/proposal/detail_ajax.html:34 msgid "Unit price" msgstr "Prix unitaire" -#: accounts/models.py:29 +#: accounts/models.py:30 msgid "Cash" msgstr "Liquide" -#: accounts/models.py:30 +#: accounts/models.py:31 msgid "Bank card" msgstr "Carte bancaire" -#: accounts/models.py:31 +#: accounts/models.py:32 msgid "Transfer" msgstr "Virement" -#: accounts/models.py:32 +#: accounts/models.py:33 msgid "Check" msgstr "Chèque" -#: accounts/models.py:35 accounts/views.py:178 accounts/views.py:252 +#: accounts/models.py:36 accounts/views.py:178 accounts/views.py:252 #: templates/expense/list.html:217 msgid "Date" msgstr "Date" -#: accounts/models.py:35 accounts/models.py:166 accounts/models.py:167 -#: accounts/models.py:169 accounts/models.py:170 accounts/models.py:171 -#: accounts/models.py:172 autoentrepreneur/forms.py:13 +#: accounts/models.py:36 accounts/models.py:167 accounts/models.py:168 +#: accounts/models.py:170 accounts/models.py:171 accounts/models.py:172 +#: accounts/models.py:173 autoentrepreneur/forms.py:13 #: autoentrepreneur/models.py:108 autoentrepreneur/models.py:163 #: project/models.py:36 project/models.py:176 project/models.py:177 #: project/models.py:179 project/models.py:180 msgid "format: mm/dd/yyyy" msgstr "format : jj/mm/aaaa" -#: accounts/models.py:36 backup/views.py:134 project/models.py:173 +#: accounts/models.py:37 backup/views.py:134 project/models.py:173 #: templates/expense/list.html:218 templates/project/detail.html:29 msgid "Reference" msgstr "Référence" -#: accounts/models.py:37 accounts/views.py:178 templates/expense/list.html:219 +#: accounts/models.py:38 accounts/views.py:178 templates/expense/list.html:219 msgid "Supplier" msgstr "Fournisseur" -#: accounts/models.py:39 accounts/models.py:168 accounts/views.py:178 +#: accounts/models.py:40 accounts/models.py:169 accounts/views.py:178 #: accounts/views.py:252 backup/views.py:135 templates/expense/list.html:221 #: templates/invoice/list.html:26 msgid "Payment type" msgstr "Type de paiement" -#: accounts/models.py:40 templates/expense/list.html:222 +#: accounts/models.py:41 templates/expense/list.html:222 msgid "Description" msgstr "Description" -#: accounts/models.py:56 +#: accounts/models.py:57 msgid "Edited" msgstr "Éditée" -#: accounts/models.py:57 newsletter/models.py:17 project/models.py:131 +#: accounts/models.py:58 newsletter/models.py:17 project/models.py:131 #: templates/newsletter/email_users.html:25 msgid "Sent" msgstr "Envoyé" -#: accounts/models.py:58 autoentrepreneur/models.py:43 core/views.py:488 +#: accounts/models.py:59 autoentrepreneur/models.py:43 core/views.py:488 msgid "Paid" msgstr "Payée" -#: accounts/models.py:162 accounts/views.py:252 backup/views.py:134 +#: accounts/models.py:163 accounts/views.py:252 backup/views.py:134 #: project/forms.py:28 project/models.py:32 project/models.py:111 #: templates/invoice/list.html:23 templates/project/list.html:20 msgid "Customer" msgstr "Client" -#: accounts/models.py:164 autoentrepreneur/models.py:107 backup/views.py:134 +#: accounts/models.py:165 autoentrepreneur/models.py:107 backup/views.py:134 #: bugtracker/models.py:35 project/forms.py:29 project/models.py:112 #: project/models.py:174 templates/backup/index.html:24 #: templates/backup/index.html.py:73 templates/core/subscribe.html:9 @@ -156,134 +155,157 @@ msgstr "Client" msgid "State" msgstr "État" -#: accounts/models.py:166 backup/views.py:135 templates/invoice/list.html:21 +#: accounts/models.py:167 backup/views.py:135 templates/invoice/list.html:21 msgid "Edition date" msgstr "Date d'édition" -#: accounts/models.py:167 backup/views.py:135 +#: accounts/models.py:168 backup/views.py:135 msgid "Payment date" msgstr "Date de paiement" -#: accounts/models.py:169 backup/views.py:136 templates/invoice/list.html:22 +#: accounts/models.py:170 backup/views.py:136 templates/invoice/list.html:22 msgid "Paid date" msgstr "Date de réception du paiement" -#: accounts/models.py:170 backup/views.py:136 +#: accounts/models.py:171 backup/views.py:136 msgid "Execution begin date" msgstr "Date de début d'exécution" -#: accounts/models.py:171 backup/views.py:136 +#: accounts/models.py:172 backup/views.py:136 msgid "Execution end date" msgstr "Date de fin d'exécution" -#: accounts/models.py:172 backup/views.py:137 +#: accounts/models.py:173 backup/views.py:137 msgid "Penalty date" msgstr "Date des pénalités" -#: accounts/models.py:174 backup/views.py:137 +#: accounts/models.py:175 backup/views.py:137 msgid "Discount conditions" msgstr "Conditions d'escompte" -#: accounts/models.py:182 +#: accounts/models.py:183 #, python-format msgid "invoice #%d" msgstr "facture n°%d" -#: accounts/models.py:214 +#: accounts/models.py:215 #, python-format msgid "Invoice id must be less than or equal to %d" msgstr "Le numéro de facture doit être inférieur ou égal à %d" -#: accounts/models.py:216 accounts/views.py:351 +#: accounts/models.py:217 accounts/views.py:351 msgid "Invoice id must be unique" msgstr "Le numéro de facture doit être unique" -#: accounts/models.py:226 accounts/views.py:348 +#: accounts/models.py:227 accounts/views.py:348 msgid "Amounts invoiced can't be greater than proposals remaining amounts" msgstr "" "Les montants facturés ne peuvent pas dépasser les montants restants des devis" -#: accounts/models.py:247 +#: accounts/models.py:262 #, python-format msgid "invoice_%(invoice_id)d.pdf" msgstr "facture_%(invoice_id)d.pdf" -#: accounts/models.py:250 +#: accounts/models.py:265 #, python-format msgid "Invoice #%(invoice_id)d" msgstr "Facture #%(invoice_id)d" -#: accounts/models.py:354 project/models.py:374 +#: accounts/models.py:369 project/models.py:374 #, python-format msgid "Date : %s" msgstr "Date : %s" -#: accounts/models.py:364 +#: accounts/models.py:379 #, python-format msgid "INVOICE #%d" msgstr "FACTURE N°%d" -#: accounts/models.py:370 faq/models.py:5 project/models.py:390 -#: project/models.py:544 templates/invoice/detail.html:68 +#: accounts/models.py:387 faq/models.py:5 project/models.py:390 +#: project/models.py:544 templates/invoice/detail.html:80 #: templates/proposal/detail.html:64 templates/proposal/detail_ajax.html:31 msgid "Label" msgstr "Libellé" -#: accounts/models.py:370 project/models.py:390 -#: templates/core/sales_widget.html:38 -msgid "Total" -msgstr "Total" +#: accounts/models.py:387 +msgid "Total excl tax" +msgstr "Total HT" + +#: accounts/models.py:389 templates/invoice/detail.html:31 +msgid "VAT" +msgstr "TVA" -#: accounts/models.py:426 +#: accounts/models.py:462 #, python-format msgid "Payment date : %s" msgstr "Date de paiement : %s" -#: accounts/models.py:427 +#: accounts/models.py:463 #, python-format msgid "Penalty begins on : %s" msgstr "Pénalité à compter du : %s" -#: accounts/models.py:428 +#: accounts/models.py:464 #, python-format msgid "Penalty rate : %s" msgstr "Taux de pénalité : %s" -#: accounts/models.py:429 +#: accounts/models.py:465 #, python-format msgid "Discount conditions : %s" msgstr "Conditions d'escompte : %s" -#: accounts/models.py:432 +#: accounts/models.py:468 #, python-format msgid "IBAN/BBAN : %s" msgstr "IBAN/RIB : %s" -#: accounts/models.py:434 +#: accounts/models.py:470 #, python-format msgid "BIC/SWIFT : %s" msgstr "BIC/SWIFT : %s" -#: accounts/models.py:438 project/models.py:447 +#: accounts/models.py:473 #, python-format -msgid "TOTAL excl. VAT : %(amount)s %(currency)s" +msgid "Total excl tax : %(amount)s %(currency)s" +msgstr "Total HT : %(amount)s %(currency)s" + +#: accounts/models.py:485 +#, python-format +msgid "VAT %(vat_rate)s%% : %(vat_amount)s %(currency)s" +msgstr "TVA %(vat_rate)s%% : %(vat_amount)s %(currency)s" + +#: accounts/models.py:494 +#, python-format +msgid "TOTAL incl tax : %(amount)s %(currency)s" +msgstr "TOTAL TTC : %(amount)s %(currency)s" + +#: accounts/models.py:496 +#, python-format +msgid "TOTAL excl tax : %(amount)s %(currency)s" msgstr "TOTAL HT : %(amount)s %(currency)s" -#: accounts/models.py:443 project/models.py:452 +#: accounts/models.py:505 project/models.py:452 #, python-format msgid "Execution dates : %(begin_date)s to %(end_date)s" msgstr "Date d'exécution : %(begin_date)s au %(end_date)s" -#: accounts/models.py:459 templates/invoice/detail.html:69 +#: accounts/models.py:522 accounts/models.py:523 accounts/models.py:524 +#, python-format +msgid "%s%%" +msgstr "%s%%" + +#: accounts/models.py:528 templates/invoice/detail.html:81 #: templates/invoice/edit.html:19 templates/invoice/edit.html.py:84 msgid "Proposal" msgstr "Devis" -#: accounts/models.py:460 +#: accounts/models.py:529 msgid "Balance payments for the proposal" msgstr "Solde le devis" -#: accounts/models.py:460 +#: accounts/models.py:529 msgid "" "\"Balancing payments for the proposal\" means there will be no future " "invoices for the selected proposal. Thus the amount remaining to invoice for " @@ -295,6 +317,10 @@ msgstr "" "à zero et son état passera à \"soldé\" quand toutes les factures auront été " "payées." +#: accounts/models.py:530 +msgid "Vat" +msgstr "Tva" + #: accounts/views.py:61 templates/core/charts_widget.html:37 msgid "Expenses" msgstr "Dépenses" @@ -574,12 +600,23 @@ msgstr "" "apparaîtra à la place de vos informations personnelles sur les devis et " "factures. Largeur/Hauteur maximales : 252x137" -#: autoentrepreneur/models.py:185 autoentrepreneur/tests.py:716 +#: autoentrepreneur/models.py:172 +msgid "VAT number" +msgstr "N° TVA" + +#: autoentrepreneur/models.py:172 +msgid "" +"If you overrun the second sales limit, you need to declare VAT. Filling this " +"field enables VAT management on invoices" +msgstr "Si vous dépassez le second plafond, vous devez déclarer la TVA. " +"Remplir ce champ active la gestion de la TVA sur les factures" + +#: autoentrepreneur/models.py:186 autoentrepreneur/tests.py:716 #, python-format msgid "You've just unregistered from %(site)s" msgstr "Vous venez de vous désinscrire de %(site)s" -#: autoentrepreneur/models.py:186 +#: autoentrepreneur/models.py:187 #, python-format msgid "" "You have left the site and your data will been deleted in " @@ -596,7 +633,7 @@ msgstr "" "Notre service évolue en permanence et s'il ne correspond pas à vos besoins " "aujourd'hui, vous pouvez revenir le tester plus tard." -#: autoentrepreneur/models.py:192 +#: autoentrepreneur/models.py:193 #: templates/registration/activation_email.txt:3 #: templates/registration/password_reset_email.html:13 #, python-format @@ -754,7 +791,7 @@ msgid "User" msgstr "Utilisateur" #: bugtracker/models.py:31 faq/models.py:17 project/models.py:545 -#: templates/invoice/detail.html:71 templates/issue/list.html:21 +#: templates/invoice/detail.html:83 templates/issue/list.html:21 #: templates/message/list.html:20 templates/proposal/detail.html:65 #: templates/proposal/detail_ajax.html:32 msgid "Category" @@ -1681,6 +1718,10 @@ msgstr "Devis %(reference)s" msgid "PROPOSAL %s" msgstr "DEVIS %s" +#: project/models.py:390 templates/core/sales_widget.html:38 +msgid "Total" +msgstr "Total" + #: project/models.py:444 #, python-format msgid "Proposal valid through : %s" @@ -1691,6 +1732,11 @@ msgstr "Devis valable jusqu'au : %s" msgid "Payment delay : %s" msgstr "Délai de paiement : %s" +#: project/models.py:447 +#, python-format +msgid "TOTAL excl. VAT : %(amount)s %(currency)s" +msgstr "TOTAL HT : %(amount)s %(currency)s" + #: project/models.py:465 project/models.py:493 msgid "reference" msgstr "référence" @@ -1885,7 +1931,7 @@ msgstr "Affaires" #: templates/menu.html:29 templates/menu.html.py:65 #: templates/contact/edit.html:152 templates/expense/list.html:235 -#: templates/invoice/edit.html:157 templates/proposal/edit.html:217 +#: templates/invoice/edit.html:158 templates/proposal/edit.html:217 msgid "Add" msgstr "Ajouter" @@ -2036,7 +2082,7 @@ msgid "No master aggreements" msgstr "Aucun contrat-cadre" #: templates/contact/edit.html:179 templates/contract/edit.html:78 -#: templates/core/settings_edit.html:102 templates/invoice/edit.html:163 +#: templates/core/settings_edit.html:105 templates/invoice/edit.html:164 #: templates/newsletter/email_users.html:13 templates/project/edit.html:23 #: templates/proposal/edit.html:257 msgid "Save" @@ -2062,7 +2108,7 @@ msgstr "suivant" msgid "No contacts" msgstr "Aucun contact" -#: templates/contract/detail.html:28 templates/invoice/detail.html:58 +#: templates/contract/detail.html:28 templates/invoice/detail.html:70 #: templates/proposal/detail.html:54 templates/proposal/detail.html.py:124 msgid "download pdf file" msgstr "télécharger le fichier pdf" @@ -2392,33 +2438,45 @@ msgstr "revenir en haut" msgid "Invoice for %(customer)s" msgstr "Facture pour %(customer)s" -#: templates/invoice/detail.html:58 templates/proposal/detail.html:54 +#: templates/invoice/detail.html:25 +msgid "Amount excluding tax" +msgstr "Montant HT" + +#: templates/invoice/detail.html:36 +msgid "Amount including tax" +msgstr "Montant TTC" + +#: templates/invoice/detail.html:70 templates/proposal/detail.html:54 msgid "Exported file" msgstr "Fichier exporté" -#: templates/invoice/detail.html:62 templates/invoice/edit.html:143 +#: templates/invoice/detail.html:74 templates/invoice/edit.html:143 #: templates/proposal/detail.html:58 templates/proposal/edit.html:204 msgid "Detail rows" msgstr "Lignes de détail" -#: templates/invoice/detail.html:70 +#: templates/invoice/detail.html:82 msgid "Balance payments" msgstr "Solde les paiements" -#: templates/invoice/detail.html:74 templates/proposal/detail.html:68 +#: templates/invoice/detail.html:86 templates/proposal/detail.html:68 #: templates/proposal/detail_ajax.html:35 msgid "Total price" msgstr "Prix total" -#: templates/invoice/detail.html:82 templates/newsletter/email_users.html:35 +#: templates/invoice/detail.html:88 +msgid "VAT rate" +msgstr "taux de TVA" + +#: templates/invoice/detail.html:97 templates/newsletter/email_users.html:35 msgid "yes" msgstr "oui" -#: templates/invoice/detail.html:82 templates/newsletter/email_users.html:35 +#: templates/invoice/detail.html:97 templates/newsletter/email_users.html:35 msgid "no" msgstr "non" -#: templates/invoice/detail.html:93 templates/proposal/detail.html:85 +#: templates/invoice/detail.html:111 templates/proposal/detail.html:85 #: templates/proposal/detail_ajax.html:52 msgid "No rows" msgstr "Aucune ligne" @@ -2602,7 +2660,9 @@ msgstr "Connexion" #: templates/registration/login.html:49 msgid "This is a demo version. Login and password are \"demo\"" -msgstr "Ceci est une version de démonstration. Les nom d'utilisateur et mot de passe sont \"demo\"" +msgstr "" +"Ceci est une version de démonstration. Les nom d'utilisateur et mot de passe " +"sont \"demo\"" #: templates/registration/login.html:51 msgid "Not yet registered ?" diff --git a/templates/core/settings_edit.html b/templates/core/settings_edit.html index bf775ea..84c84df 100644 --- a/templates/core/settings_edit.html +++ b/templates/core/settings_edit.html @@ -95,7 +95,7 @@ {% endwith %} - {% with form.vat_enabled as field %} + {% with form.vat_number as field %} {% include "field.html" %} {% endwith %} {% endwith %} diff --git a/templates/invoice/detail.html b/templates/invoice/detail.html index 12a6f68..b3f33ed 100644 --- a/templates/invoice/detail.html +++ b/templates/invoice/detail.html @@ -3,7 +3,7 @@ {% load modeltags %} {% load arithmetictags %} -{% block title %}{% url customer_detail invoice.customer.id as customer_url %}{% blocktrans with invoice.customer as customer and customer_url as customer_url %}Invoice for {{ customer }}{% endblocktrans %}{% endblock %} +{% block title %}{% url contact_detail invoice.customer.id as customer_url %}{% blocktrans with invoice.customer as customer and customer_url as customer_url %}Invoice for {{ customer }}{% endblocktrans %}{% endblock %} {% block context_menu %}
{% trans "Edit" %}
@@ -25,15 +25,15 @@ {{ invoice.amount }} - {% if user.get_profile.vat_enabled %} + {% if user.get_profile.vat_number %}
- {{ invoice.get_vat }} + {{ invoice.get_vat|floatformat:2 }}
- {{ invoice.amount_including_tax }} + {{ invoice.amount_including_tax|floatformat:2 }}
{% endif %} @@ -84,7 +84,7 @@

{% trans "Detail rows" %}

{% trans "Quantity" %} {% trans "Unit price" %} {% trans "Total price" %} - {% if user.get_profile.vat_enabled %} + {% if user.get_profile.vat_number %} {% trans "VAT rate" %} {% endif %} @@ -97,9 +97,9 @@

{% trans "Detail rows" %}

{% if invoicerow.balance_payments %}{% trans "yes" %}{% else %}{% trans "no" %}{% endif %} {{ invoicerow.get_category_display }} {{ invoicerow.quantity|floatformat }} - {{ invoicerow.unit_price|floatformat }} - {{ invoicerow.amount|floatformat }} - {% if user.get_profile.vat_enabled %} + {{ invoicerow.unit_price|floatformat:2 }} + {{ invoicerow.amount|floatformat:2 }} + {% if user.get_profile.vat_number %} {{ invoicerow.vat_rate|default:'' }} {% endif %} diff --git a/templates/invoice/edit.html b/templates/invoice/edit.html index f39cece..5336b01 100644 --- a/templates/invoice/edit.html +++ b/templates/invoice/edit.html @@ -151,7 +151,7 @@ {{ form.quantity.errors|format_errors:form.quantity.label }} {{ form.unit_price.errors|format_errors:form.unit_price.label }} {{ form.vat_rate.errors|format_errors:form.vat_rate.label }} - {{ form.ownedobject_ptr }}{{ form.label.label }} : {{ form.label }} {{ form.proposal }} {{ form.balance_payments.label }} : {{ form.balance_payments }} {{ form.category }} {{ form.quantity.label }} : {{ form.quantity }} {{ form.unit_price.label }} : {{ form.unit_price }}{% if user.get_profile.vat_enabled %} {{ form.vat_rate }}{% endif %}{{ form.DELETE }} + {{ form.ownedobject_ptr }}{{ form.label.label }} : {{ form.label }} {{ form.proposal }} {{ form.balance_payments.label }} : {{ form.balance_payments }} {{ form.category }} {{ form.quantity.label }} : {{ form.quantity }} {{ form.unit_price.label }} : {{ form.unit_price }}{% if user.get_profile.vat_number %} {{ form.vat_rate }}{% endif %}{{ form.DELETE }} {% endfor %}