def make_invoice(self, issuer): """Creates an invoice based on the current quotation/purchase order""" from invoicing.models import Invoice, InvoiceRevision # Initialize the invoice invoice = Invoice( full_init=False, tenant=self.tenant, account_type=self.account_type, issuer=issuer, organization=self.organization, contact=self.contact, related_to=self, group=self.group, attachments=self.attachments ) # Save the invoice, based on the quotation/purchase order inv_data = invoice.add_revision(revision=InvoiceRevision(based_on=self.current_revision)) inv_data.state = invoice.state inv_data.issuer = issuer inv_data.issue_date = datetime_now() inv_data.invoicing_date = datetime.date.today() inv_data.due_date = get_due_date(inv_data.invoicing_date, self.tenant.tenant_settings.invoicing.payment_conditions) invoice.save() # Update state if self.is_quotation(): self.state = QUOTATION_STATES.INVOICED elif self.is_purchase_order(): self.state = PURCHASE_ORDER_STATES.INVOICED self.save() return invoice
def create(request): choice_member = Member.objects.all() if not request.POST: return render(request, 'invoice.html', {'choice_member': choice_member}) else: member_id = request.POST['member_id'] invoice_number_max = Invoice.objects.all().aggregate( Max('invoice_number'))['invoice_number__max'] invoice_number = invoice_number_max + 1 member = Member.objects.get(pk=member_id) usages = Usage.objects.filter(member=member, valid=True, invoice=None) total_amount = usages.aggregate(total=Sum('total_price'))['total'] invoice = Invoice(amount=total_amount, member=member, invoice_number=invoice_number) invoice.save() usages.update(invoice=invoice) usages = Usage.objects.filter(invoice=invoice) return render( request, 'invoice.html', { 'usages': usages, 'member_info': member, 'choice_member': choice_member, 'invoice': invoice })
def make_invoice(self, issuer): """Creates an invoice based on the current quotation/purchase order""" from invoicing.models import Invoice, InvoiceRevision # Initialize the invoice invoice = Invoice(full_init=False, tenant=self.tenant, account_type=self.account_type, issuer=issuer, organization=self.organization, contact=self.contact, related_to=self, group=self.group, attachments=self.attachments) # Save the invoice, based on the quotation/purchase order inv_data = invoice.add_revision(revision=InvoiceRevision( based_on=self.current_revision)) inv_data.state = invoice.state inv_data.issuer = issuer inv_data.issue_date = datetime_now() inv_data.invoicing_date = datetime.date.today() inv_data.due_date = get_due_date( inv_data.invoicing_date, self.tenant.tenant_settings.invoicing.payment_conditions) invoice.save() # Update state if self.is_quotation(): self.state = QUOTATION_STATES.INVOICED elif self.is_purchase_order(): self.state = PURCHASE_ORDER_STATES.INVOICED self.save() return invoice
def test_get_invoice(client): with db.connection() as DB: invoice = Invoice(amount=5, description='Test invoice') DB.add(invoice) response = client.get('/invoices/{0}/'.format(invoice.id)) data = response.json data['amount'] = Decimal(data['amount']) data['amount_remaining'] = Decimal(data['amount_remaining']) invoice_details = invoice.to_json() invoice_details['created_at'] = invoice_details['created_at'].strftime(DATE_FORMAT) # noqa invoice_details['updated_at'] = invoice_details['updated_at'].strftime(DATE_FORMAT) # noqa assert data == invoice_details
def get_exportable_documents(export): documents = [] if 'QUOTATION' in export.documents_types: full_export = Quotation.full_export(export.tenant, export.from_date, export.to_date) documents.append(full_export) serializer = QuotationCSVSerializer() documents.append(( [serializer.serialize(full_export[0])], lambda buf: '{0}/{1}.csv'.format(_('Quotations'), _('Quotations')), lambda buf: buf )) if 'INVOICE' in export.documents_types: full_export = Invoice.full_export(export.tenant, export.from_date, export.to_date) documents.append(full_export) serializer = InvoiceCSVSerializer() documents.append(( [serializer.serialize(full_export[0])], lambda buf: '{0}/{1}.csv'.format(_('Invoices'), _('Invoices')), lambda buf: buf )) if 'CREDIT_NOTE' in export.documents_types: full_export = CreditNote.full_export(export.tenant, export.from_date, export.to_date) documents.append(full_export) serializer = CreditNoteCSVSerializer() documents.append(( [serializer.serialize(full_export[0])], lambda buf: '{0}/{1}.csv'.format(_('Credit notes'), _('Credit notes')), lambda buf: buf )) return documents
def forwards(self, orm): from invoicing.models import InvoiceBaseGroup # Process quotations for quotation in Quotation.objects(): group = InvoiceBaseGroup(tenant=quotation.tenant, quotation=quotation).save() if quotation._data.get('down_payments'): group.down_payment_invoices = dereference(quotation._data.get('down_payments'), DownPaymentInvoice) for down_payment_invoice in group.down_payment_invoices: down_payment_invoice.update(set__group=group) if quotation._data.get('related_invoice'): group.invoice = dereference(quotation._data.get('related_invoice'), Invoice) group.invoice.update(set__group=group) if quotation._data.get('related_invoices_cancelled'): group.invoices_cancelled = dereference(quotation._data.get('related_invoices_cancelled'), Invoice) for invoice_cancelled in group.invoices_cancelled: invoice_cancelled.update(set__group=group) group.save() quotation.update(set__group=group) # Process invoices and down-payment invoices for invoice in Invoice.objects(): if not invoice.group.id: # Can only be an invoice: down-payment invoices are linked to quotations, so group is set group = InvoiceBaseGroup(tenant=quotation.tenant, invoice=invoice).save() invoice.update(set__group=group) else: group = invoice.group if invoice._data.get('related_credit_note'): group.credit_notes = [dereference(invoice._data.get('related_credit_note'), CreditNote)] for credit_note in group.credit_notes: credit_note.update(set__group=group, set__related_to=invoice) group.save()
def test_list_invoices(client): with db.connection() as DB: invoice = Invoice(amount=5, description='Test invoice') DB.add(invoice) response = client.get('/invoices/') data = response.json for n in range(len(data)): data[n]['amount'] = Decimal(data[n]['amount']) data[n]['amount_remaining'] = Decimal(data[n]['amount_remaining']) invoice_details = invoice.to_json() invoice_details['created_at'] = invoice_details['created_at'].strftime(DATE_FORMAT) # noqa invoice_details['updated_at'] = invoice_details['updated_at'].strftime(DATE_FORMAT) # noqa assert data == [invoice_details]
def preview(request): #Todo SELECT DISTINCT member from Usage where factNumber is null choice_member = Member.objects.exclude(usage=None) if not request.POST: return render(request, 'invoice.html', {'choice_member': choice_member}) else: member_id = request.POST['member_id'] invoice_number_max = Invoice.objects.all().aggregate( Max('invoice_number'))['invoice_number__max'] invoice_number = invoice_number_max + 1 member = Member.objects.get(pk=member_id) usages = Usage.objects.filter(member=member, valid=True, invoice=None) total_amount = usages.aggregate(total=Sum('total_price'))['total'] or 0 usages_annotated = usages.values( 'resource__name', 'resource__unit__name', 'unit_price', 'project__name').annotate( qty=Sum('qty'), total_price=Sum('total_price')).order_by('project__name') # information about machine hours for animators amount_machine_before = AccountEntry.objects.filter( member=member).aggregate(total=Sum('amount_machine'))['total'] or 0 amount_machine_usages = usages.filter( resource__payable_by_animation_hours=True).aggregate( total=Sum('total_price'))['total'] or 0 deduction = min(amount_machine_before, amount_machine_usages) amount_machine_after = amount_machine_before - deduction amount_due = total_amount - deduction invoice = Invoice(amount=total_amount, amount_deduction=deduction, amount_due=amount_due, member=member, invoice_number=invoice_number) print(total_amount) return render( request, 'invoice.html', { 'usages': usages, 'usages_anotated': usages_annotated, 'member_info': member, 'choice_member': choice_member, 'invoice': invoice, 'amount_machine_before': amount_machine_before, 'amount_machine_after': amount_machine_after, 'amount_machine_usages': amount_machine_usages })
def setUpClass(cls): from contacts.models import Organization from invoicing.models import Invoice, Currency from notification import models as notification_models super(NotificationResourceTest, cls).setUpClass() cls.payload_kwargs = { 'data': {}, 'HTTP_X_TENANT': settings.TENANT.slug } # Retrieve currency which will be referenced eur = Currency.objects.get(symbol='EUR') # Create an organization which will be referenced organization = Organization.objects.create(tenant=settings.TENANT, creator=settings.VOSAE_USER, corporate_name="My Company", private=False) # Create deps documents invoice = Invoice(tenant=settings.TENANT, account_type='RECEIVABLE', issuer=settings.VOSAE_USER) invoice.current_revision.currency = eur.get_snapshot() invoice.current_revision.organization = organization invoice.save() # Create a notification notification = notification_models.InvoiceChangedState() notification.previous_state = "DRAFT" notification.new_state = "REGISTERED" notification.issuer = settings.VOSAE_USER notification.recipient = settings.VOSAE_USER notification.tenant = settings.TENANT notification.invoice = invoice notification.save() cls.cached_data = { 'invoice_uri': cls.resourceDetailURI('invoice', unicode(invoice.id)), 'notification_uri': cls.resourceDetailURI('notification', unicode(notification.id)) } cls.created_documents = [organization, notification, invoice]
def setUpClass(cls): from contacts.models import Organization from invoicing.models import Invoice, Currency from notification import models as notification_models super(NotificationResourceTest, cls).setUpClass() cls.payload_kwargs = { 'data': {}, 'HTTP_X_TENANT': settings.TENANT.slug } # Retrieve currency which will be referenced eur = Currency.objects.get(symbol='EUR') # Create an organization which will be referenced organization = Organization.objects.create(tenant=settings.TENANT, creator=settings.VOSAE_USER, corporate_name="My Company", private=False) # Create deps documents invoice = Invoice( tenant=settings.TENANT, account_type='RECEIVABLE', issuer=settings.VOSAE_USER ) invoice.current_revision.currency = eur.get_snapshot() invoice.current_revision.organization = organization invoice.save() # Create a notification notification = notification_models.InvoiceChangedState() notification.previous_state = "DRAFT" notification.new_state = "REGISTERED" notification.issuer = settings.VOSAE_USER notification.recipient = settings.VOSAE_USER notification.tenant = settings.TENANT notification.invoice = invoice notification.save() cls.cached_data = { 'invoice_uri': cls.resourceDetailURI('invoice', unicode(invoice.id)), 'notification_uri': cls.resourceDetailURI('notification', unicode(notification.id)) } cls.created_documents = [organization, notification, invoice]
def collection_post(self): try: invoice = self.request.json date_str = invoice.get('date') invoice['date'] = parser.parse(date_str) DBSession.add(Invoice.from_json(invoice)) invoice['date'] = date_str self.request.response.status_code = 201 return invoice except Exception as e: return {'error': str(e)}
def test_invoiced_true(self): unit = ResourceUnit(name="heure") resource = Resource(name="Laser", slug="laser", unit=unit, price_not_member=2, price_member=1) usage = Usage(resource=resource, qty=5) invoice = Invoice(invoice_number=100, amount=50) usage.invoice = invoice self.assertTrue(usage.invoiced())
def forwards(self, orm): # Quotation for quotation in Quotation.objects(): quotation._changed_fields = [ 'tenant', 'issuer', 'organization', 'contact', 'attachments', 'related_invoice', 'down_payments', 'subscribers' ] set_embedded_changed_fields(quotation) quotation.save() # Invoice for invoice in Invoice.objects(): invoice._changed_fields = [ 'tenant', 'issuer', 'organization', 'contact', 'attachments', 'related_quotation', 'payments', 'subscribers' ] set_embedded_changed_fields(invoice) invoice.save() # DownPaymentInvoice for down_payment_invoice in DownPaymentInvoice.objects(): down_payment_invoice._changed_fields = [ 'tenant', 'issuer', 'organization', 'contact', 'attachments', 'related_quotation', 'payments', 'tax_applied', 'subscribers' ] set_embedded_changed_fields(down_payment_invoice) down_payment_invoice.save() # CreditNote for credit_note in CreditNote.objects(): credit_note._changed_fields = [ 'tenant', 'issuer', 'organization', 'contact', 'attachments', 'related_invoice', 'subscribers' ] set_embedded_changed_fields(credit_note) credit_note.save() # Item for item in Item.objects(): item._changed_fields = ['tenant', 'currency', 'tax'] item.save() # Payment for payment in Payment.objects(): payment._changed_fields = ['tenant', 'issuer', 'currency'] payment.save() # Tax for tax in Tax.objects(): tax._changed_fields = ['tenant'] tax.save()
def test_pay_invoice(client): with db.connection() as DB: invoice = Invoice(amount=5, description='Test invoice') DB.add(invoice) request_data = {'amount': 5} response = client.post('/invoices/{0}/pay/'.format(invoice.id), json=request_data) data = response.json print(data) assert data['amount_remaining'] == 0 assert data['status'] == 'Paid'
def forwards(self, orm): # Quotation for quotation in Quotation.objects(): quotation._changed_fields = ['tenant', 'issuer', 'organization', 'contact', 'attachments', 'related_invoice', 'down_payments', 'subscribers'] set_embedded_changed_fields(quotation) quotation.save() # Invoice for invoice in Invoice.objects(): invoice._changed_fields = ['tenant', 'issuer', 'organization', 'contact', 'attachments', 'related_quotation', 'payments', 'subscribers'] set_embedded_changed_fields(invoice) invoice.save() # DownPaymentInvoice for down_payment_invoice in DownPaymentInvoice.objects(): down_payment_invoice._changed_fields = ['tenant', 'issuer', 'organization', 'contact', 'attachments', 'related_quotation', 'payments', 'tax_applied', 'subscribers'] set_embedded_changed_fields(down_payment_invoice) down_payment_invoice.save() # CreditNote for credit_note in CreditNote.objects(): credit_note._changed_fields = ['tenant', 'issuer', 'organization', 'contact', 'attachments', 'related_invoice', 'subscribers'] set_embedded_changed_fields(credit_note) credit_note.save() # Item for item in Item.objects(): item._changed_fields = ['tenant', 'currency', 'tax'] item.save() # Payment for payment in Payment.objects(): payment._changed_fields = ['tenant', 'issuer', 'currency'] payment.save() # Tax for tax in Tax.objects(): tax._changed_fields = ['tenant'] tax.save()
def prepare(request, create=False): member_id = request.POST['member_id'] invoice_number_max = Invoice.objects.all().aggregate(Max('invoice_number'))['invoice_number__max'] or 0 invoice_number = invoice_number_max + 1 member = Member.objects.get(pk=member_id) usages = Usage.objects.filter(member=member, valid=True, invoice=None) total_amount = usages.aggregate(total=Sum('total_price'))['total'] or 0 usages_annotated = usages.values('resource__name', 'resource__unit__name', 'unit_price', 'project__name').annotate(qty=Sum('qty'), total_price=Sum('total_price') ).order_by('project__name') balance = AccountEntry.objects.filter(member=member).aggregate(machine=Sum('amount_machine'), cash=Sum('amount_cash')) print(balance) # information about machine hours balance amount_machine_before = balance['machine'] or 0 amount_machine_usages = usages.filter(resource__payable_by_animation_hours=True).aggregate( total=Sum('total_price'))['total'] or 0 deduction_machine = min(amount_machine_before, amount_machine_usages) amount_machine_after = amount_machine_before - deduction_machine # information about cash balance cash_before = balance['cash'] or 0 remaining_amount = total_amount - deduction_machine deduction_cash = min(cash_before, remaining_amount) cash_after = cash_before - deduction_cash deduction = deduction_machine + deduction_cash invoice = Invoice(amount=total_amount, amount_deduction=deduction, member=member, invoice_number=invoice_number) print(total_amount) if create: invoice.save() if deduction > 0: AccountEntry.objects.create(member=member, amount_machine=-deduction_machine, amount_cash=-deduction_cash, invoice=invoice) usages.update(invoice=invoice) usages = Usage.objects.filter(invoice=invoice) return {'usages': usages, 'usages_anotated': usages_annotated, 'member_info': member, 'invoice': invoice, 'amount_machine_before': amount_machine_before, 'amount_machine_after': amount_machine_after, 'amount_machine_usages': amount_machine_usages, 'deduction_machine': deduction_machine, 'cash_before': cash_before, 'cash_after': cash_after, 'deduction_cash': deduction_cash }
def generate_child_invoice(**kwargs): """ function to generate a new child invoice based upon the parent """ try: parent = Invoice.objects.get( invoice_number=kwargs['args']) # invoice instance child = Invoice() child.client = parent.client child.tax = parent.tax child.discount_rate = parent.discount_rate child.issued_by = parent.issued_by child.invoice_status = Invoice.INVOICE_STATUS[1][0] child.recurring = Invoice.RECURRING[0][0] child.parent_invoice = parent child.date_due = timezone.now().date() child.save() # save the new m2m intermediary model parent_units = Units.objects.filter(invoice=parent) for item in parent_units.iterator(): new = Units() new.invoice = child new.item = item.item new.quantity = item.quantity new.save() # generate pdf and send email (note: email=False, as already being dispatched due to creation of new PDF) invoicing_view.pdf_gen_or_fetch_or_email( invoice_number=child.invoice_number, type=invoicing_view.PDF_TYPES.get('invoice'), email=False, regenerate=False) return True except Invoice.DoesNotExist: return None
def main(): """ Find subscriptions that are due to be invoiced, generate the invoices, update the accounts, update the subscription and kill all humans """ invoices = [] try: # Get current subscriptions and subscriptions that have not been (fully) invoiced and are due to be invoiced. subscriptions = Subscription.objects.filter(active = 1) subscriptions = subscriptions.filter(Q(invoiced_until_date__isnull = True) | Q(invoiced_until_date__gte = F('start_date'))) subscriptions = subscriptions.filter(Q(end_date__isnull = True) | Q(invoiced_until_date__lte = F('end_date'))) subscriptions = subscriptions.filter(Q(invoiced_until_date__isnull = True) | Q(invoiced_until_date__lte = datetime.utcnow())) for subscription in subscriptions: invoice = Invoice() invoice.customer = subscription.customer invoice.date = date.today() invoice.save() if subscription.invoiced_until_date: start_date = subscription.invoiced_until_date else: start_date = subscription.start_date # Invoice every invoice period that needs to be invoiced. while start_date < datetime.utcnow(): # # Calculate the date until which we are invoicing # if subscription.product.invoice_interval == 0: subscription.invoiced_until_date = start_date + timedelta(days = subscription.product.invoice_interval_count * subscription.intervals_per_invoice) if subscription.product.invoice_interval == 1: subscription.invoiced_until_date = start_date + timedelta(weeks = subscription.product.invoice_interval_count * subscription.intervals_per_invoice) elif subscription.product.invoice_interval == 2: months = subscription.product.invoice_interval_count * subscription.intervals_per_invoice month = start_date.month + months % 12 year = start_date.year + int(months / 12) subscription.invoiced_until_date = datetime(year, month, start_date.day, 0, 0, 0) elif subscription.product.invoice_interval == 3: quarters = subscription.product.invoice_interval_count * subscription.intervals_per_invoice month = start_date.month + (quarters * 3) % 12 year = start_date.year + int((quarters * 3) / 12) subscription.invoiced_until_date = datetime(year, month, start_date.day, 0, 0, 0) elif subscription.product.invoice_interval == 4: years = subscription.product.invoice_interval_count * subscription.intervals_per_invoice subscription.invoiced_until_date = datetime(start_date.year + years, month, day, 0, 0, 0) # Add the subscription lines to the invoice invoiceitem = InvoiceItem() invoiceitem.invoice = invoice invoiceitem.item = None # Only used for products with serials invoiceitem.product = subscription.product invoiceitem.period = start_date.strftime("%Y-%m-%d") + " - " + subscription.invoiced_until_date.strftime("%Y-%m-%d") invoiceitem.description = subscription.extra_info invoiceitem.count = 1 invoiceitem.amount = subscription.product.get_price() if invoiceitem.amount == None: # This item has no price raise LookupError invoiceitem.amount *= subscription.intervals_per_invoice invoiceitem.vat = subscription.product.vat invoiceitem.save() start_date = subscription.invoiced_until_date # This subscription has been invoiced. subscription.save() # The transactions should be made to the VAT tables and accounts receivable invoice.book() invoices.append(invoice) except: db_trans.rollback() raise else: db_trans.commit() for invoice in invoices: # Mail the invoice to the client if requested, otherwise put the PDF in the outgoing snailmail queue if invoice.customer.invoice_by_email: pdf = invoice.pdf() send_mail("%s <%s>" % (invoice.customer.displayname, invoice.customer.invoice_email), invoice.full_invoice_no, pdf) else: # FIXME Snailmail queue should be here pass db_trans.commit()
def update_invoices_states(): from invoicing.models import Quotation, Invoice Quotation.manage_states() Invoice.manage_states()