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...
Management Commands
===================
Invoice Lines
-------------
::
# Time records on an invoice
django-admin report-invoice-lines-time-only 1611
# Invoice lines excluding time records
django-admin report-invoice-lines-time-excluded 1611
.. tip:: Source code in ``invoice/management/commands/``
Invoice Summary
---------------
For monthly reporting, pass the primary key of the invoice number::
django-admin pandas-invoice-summary 1611
.. tip:: Probably best to use the ``SalesOrderSummaryReport``
in :ref:`sales-order-ticket-reports`
A PDF report will be created e.g. ``invoice-001611-summary.pdf``.
.. tip:: Source code in ``invoice/management/commands/``
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``.
Pandas - Invoice totals by Month for a Contact
----------------------------------------------
Display the invoice totals for a contact for the last 12 months::
django-admin pandas-contact-invoice 12
Source code `pandas-contact-invoice`_ ...
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)
Reports
=======
``TimeAnalysisByUserTicketReport`` (``invoice/reports.py``)
*Time analysis by user (by whole month)*
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
.. _`pandas-contact-invoice`: https://gitlab.com/kb/invoice/-/blob/master/invoice/management/commands/pandas-contact-invoice.py