invoice ******* .. highlight:: python https://gitlab.com/kb/invoice Icon ==== Invoice:: Time Recording:: Plan ==== I am trying to use the ``VatCode`` model in the ``invoice`` app, but we have a problem with the ``Contact`` record in the ``crm``. The ``job`` app uses ``settings.CONTACT_MODEL``, and the ``crm`` app has it's own ``Contact``. I think the proper solution is to move ``Contact`` from the ``crm`` app and put it into a ``contact`` app. Move ``TimeRecord`` from ``invoice`` to ``crm``. Update the invoice routines so they can invoice a *generic* line type. One of these types would be ``TimeRecord``. Create a new ``account`` app. Move ``VatCode`` into this ``account`` app. At a later date, move the code from ``invoice`` and ``pay`` into the ``account`` app. Credit Note =========== To create a credit note, list invoices for a contact, click *Create draft invoice (without time records)*, enter the date for the credit note. Click *Add line*, enter a **negative quantity** and a *positive price*. .. warning:: It is **not** possible to mix invoice and credit lines on one document. If you want to credit a customer, then create a separate credit note. Date ==== To create an invoice for a particular date (the ``slug`` is for the contact):: from datetime import date from crm.models import Contact from django.contrib.auth.models import User from invoice.service import InvoiceCreate invoice_date = date(2013, 12, 31) slug = 'kb' user_name = 'patrick.kimber' invoice_create = InvoiceCreate() contact = Contact.objects.get(slug=slug) user = User.objects.get(username=user_name) invoice_create.create(user, contact, invoice_date) To create the invoice PDF, follow the *PDF* instructions below... Export to R (Shiny) =================== Management command:: django-admin.py export_to_r .. note:: The source code is at `export_to_r`_ - The export file is *tab* separated. - The ``net`` column is the total net value for the line. - The ``quantity`` column is the number of items supplied. .. tip:: To get the ``price`` of a product, divide the ``net`` value by the ``quantity``. Outstanding =========== .. tip:: This code has been added to the invoice app at the following URL, ``/invoice/time/outstanding/`` Who do I need to invoice (run from project folder using ``django-admin shell``):: from invoice.models import TimeRecord qs = TimeRecord.objects.filter(invoice_line__isnull=True, billable=True).order_by('ticket__contact__slug').distinct('ticket__contact__slug') for t in qs: print(t.ticket.contact.slug) How much time have I spent on a ticket:: # edit the ticket numbers in the source code django-admin.py ticket_time_csv PDF === To create the PDF for an invoice:: from invoice.models import Invoice from invoice.service import InvoicePrint invoice_number = 53 invoice = Invoice.objects.get(pk=invoice_number) invoice.contact.name invoice.pdf = None InvoicePrint().create_pdf(invoice, header_image=None) Templates and Mixins ==================== .. tip:: All projects which use the ``invoice`` app should provide a *detail* URL named ``invoice.detail``. ``BatchInvoiceListView`` ------------------------ :: from invoice.views import BatchInvoiceListMixin class BatchInvoiceListView( LoginRequiredMixin, StaffuserRequiredMixin, BatchInvoiceListMixin, BaseMixin, ListView, ): template_name = "dash/batch_invoice_list.html" .. tip:: You can use ``example_invoice/templates/example/batch_invoice_list.html`` as a starting point for your own template. :: from .views import BatchInvoiceListView url( regex=r"^batch/(?P\d+)/invoice/$", view=BatchInvoiceListView.as_view(), name="invoice.batch.invoice.list", ), :: from invoice.tests.factories import BatchFactory @pytest.mark.django_db def test_batch_invoice_list(perm_check): batch = BatchFactory() url = reverse("invoice.batch.invoice.list", args=[batch.pk]) perm_check.staff(url) ``InvoiceDetailMixin`` ---------------------- :: from braces.views import LoginRequiredMixin, StaffuserRequiredMixin from django.views.generic import DetailView, TemplateView from invoice.views import InvoiceDetailMixin class InvoiceDetailView( LoginRequiredMixin, StaffuserRequiredMixin, InvoiceDetailMixin, BaseMixin, DetailView, ): template_name = "dash/invoice_detail.html" .. tip:: The ``example_invoice/templates/example/invoice_detail.html`` example template shows an example of how to use the detail view:: {% include "invoice/_invoice_detail_header.html" %} {% include "invoice/_invoice_detail.html" %} {% include "invoice/_invoice_detail_lines.html" %} .. tip:: Also see :doc:`app-magento` for the invoice detail include for the Magento app. :: from .views import InvoiceDetailView url( regex=r"^invoice/(?P\d+)/$", view=InvoiceDetailView.as_view(), name="invoice.detail", ), ``test_view_perm.py``:: from invoice.tests.factories import InvoiceFactory @pytest.mark.django_db def test_invoice_detail(perm_check): invoice = InvoiceFactory() url = reverse("invoice.detail", args=[invoice.pk]) perm_check.staff(url) InvoiceListMixin ---------------- :: from invoice.views import InvoiceListMixin class InvoiceListView( LoginRequiredMixin, StaffuserRequiredMixin, InvoiceListMixin, BaseMixin, ListView, ): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context.update({"show_download": True}) return context :: from .views import InvoiceListView url( regex=r"^invoice/$", view=InvoiceListView.as_view(), name="invoice.list" ), :: DJANGO_APPS = ( "django.contrib.humanize", :: @pytest.mark.django_db def test_invoice_list(perm_check): url = reverse("invoice.list") perm_check.staff(url) Ticket Time =========== This is a temporary management command which will download all the time records for a list of tickets. To use this command, start by restoring a backup of the data for your site. Then edit the source code for the management command:: ~/repo/dev/app/invoice/invoice/management/commands/ticket_time_csv.py Update the list of numbers to include the tickets you want in your CSV file:: tickets = ( 732, Run the management command:: django-admin.py ticket_time_csv .. _`export_to_r`: https://gitlab.com/kb/invoice/blob/master/invoice/service.py#L32