ComptaClub/src/billing/views.py

391 lines
13 KiB
Python

# coding=utf-8
from django.shortcuts import render
from django.http import HttpResponse
from django.db.models import Sum
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
# from PIL import Image
import os
from django.conf import settings
from datetime import date, datetime, timedelta
from .billing_config import loadBillingConfig
from .models import (
Client,
Contract,
Prestation,
)
def contract_listing(request):
"""
Renvoie la liste de tous les contrats.
"""
contract_list = Contract.objects.all()
context = {"contract_list": contract_list}
return render(request, "contract_listing.html", context)
def contract_detail(request, contractid):
"""
Renvoie toutes les informations relatives à un contrat, en ce y compris les prestations
relatives à celui-ci.
"""
contract = Contract.objects.get(pk=contractid)
prestation_list = contract.get_prestation.all()
prestation_count = prestation_list.count()
total = list(contract.get_prestation.all().aggregate(Sum("total_amount")).values())[
0
]
context = {
"contract": contract,
"prestation_list": prestation_list,
"prestation_count": prestation_count,
"total": total,
}
return render(request, "contract_detail.html", context)
def client_listing(request):
"""
Renvoie la liste de tous les clients.
"""
client_list = Client.objects.all()
context = {"client_list": client_list}
return render(request, "client_listing.html", context)
def prestation_listing(request):
"""
Renvoie la liste de toutes les prestations.
"""
prestation_list = Prestation.objects.all()
context = {"prestation_list": prestation_list}
return render(request, "prestation_listing.html", context)
# Est-ce que ces variables ne devraient pas être dans un fichier de configuration à part ?
Y = 841.89
X = 35
RIGHT_X = 595.27 - X
TITLED_X = 125
INDENTED_X = X + 5
INDENTED_RIGHT_X = RIGHT_X - 5
PRESTATION_COLUMN_2 = INDENTED_X + 65
PRESTATION_COLUMN_3 = INDENTED_X + 400
PRESTATION_COLUMN_4 = INDENTED_X + 455
COMMON_LINE_HEIGHT = -15
DOUBLE_LINE_HEIGHT = COMMON_LINE_HEIGHT * 2
BIG_LINE_HEIGHT = COMMON_LINE_HEIGHT * 3
HUGE_LINE_HEIGHT = COMMON_LINE_HEIGHT * 4
class MyDocument(object):
# Create the PDF object, using the response object as its "file."
# http://www.reportlab.com/docs/reportlab-userguide.pdf
# canvas.rect(x, y, width, height, stroke=1, fill=0)
# localhost:8000/billing/contract/pdf/2
def __init__(self, response):
# Create the PDF object, using the response object as its "file."
self.document = canvas.Canvas(response, pagesize=A4)
self.y = Y - X
self.amount = 0
def newline(self, height=None):
"""
Passe à la ligne, la hauteur de la ligne étant passée en paramètre.
"""
if height == DOUBLE_LINE_HEIGHT:
self.y += DOUBLE_LINE_HEIGHT
elif height == BIG_LINE_HEIGHT:
self.y += BIG_LINE_HEIGHT
elif height == HUGE_LINE_HEIGHT:
self.y += HUGE_LINE_HEIGHT
else:
self.y += COMMON_LINE_HEIGHT
# if y < 120;
# document.PageBreak()
# y = 790
def drawString(
self, x, string, font_family="Helvetica", font_decoration=None, font_size=10
):
font = font_family
if font_decoration is not None:
font += "-" + font_decoration
self.document.setFont(font, font_size)
self.document.drawString(x, self.y, string)
def drawNewLine(
self,
x,
string,
height=None,
font_family="Helvetica",
font_decoration=None,
font_size=10,
):
self.newline(height)
self.drawString(x, string, font_family, font_decoration, font_size)
def header(self, info, contract):
"""
Génère le header de la facture.
"""
# self.document.rect(X, 735, 525, 70, fill=0)
self.drawNewLine(INDENTED_X, info["NAME"])
self.document.drawRightString(
RIGHT_X, self.y, "N° de Référence : " + str(contract.reference)
)
self.drawNewLine(INDENTED_X, info["ADDRESS"])
self.drawNewLine(INDENTED_X, info["CITY"])
self.drawNewLine(INDENTED_X, info["GSM"])
self.newline(DOUBLE_LINE_HEIGHT)
def title(self, contract):
"""
Génère le "titre" de la facture.
"""
res = contract.client.contact.count("i")
res += contract.client.contact.count("t")
res += contract.client.contact.count("l")
res += contract.client.contact.count("j")
res += contract.client.contact.count("f")
# res += contract.client.contact.count('r')
self.drawString(TITLED_X, "A l'attention de")
self.drawString(194.25, contract.client.contact, font_decoration="Bold")
if contract.client.is_company:
self.drawString(
194.25 + ((len(contract.client.contact) - res / 1.5) * 5.8), " pour la"
)
self.drawNewLine(TITLED_X, contract.client.name, font_decoration="Bold")
self.drawNewLine(TITLED_X, contract.client.address)
self.drawNewLine(
TITLED_X, str(contract.client.postal_code) + " " + contract.client.city
)
self.newline()
self.drawNewLine(TITLED_X, "Concernant la/le")
self.drawString(200, contract.name, font_decoration="Bold")
self.newline()
def payementInformation(self, info, contract):
"""
Génère les informations de payement.
"""
self.newline()
height = 40
self.document.rect(X, self.y - height, RIGHT_X - X, height, fill=0)
self.drawNewLine(INDENTED_X, "N° Entreprise : " + info["COMPANY_NUMBER"])
if contract.client.company_number:
self.document.drawRightString(
INDENTED_RIGHT_X,
self.y,
"Votre N° Entreprise : " + contract.client.company_number,
)
self.drawNewLine(
INDENTED_X,
"IBAN : "
+ info["IBAN"]
+ " (BIC : "
+ info["BIC"]
+ " - "
+ info["BANK"]
+ ")",
)
self.newline(DOUBLE_LINE_HEIGHT)
def prestations(self, contract):
"""
Génère l'affichage des prestations : tableau, liste des prestations, …
"""
self.drawNewLine(X, "Prestations", font_decoration="Bold")
total = self.drawPrestationsTable(contract.get_prestation.all())
self.newline(DOUBLE_LINE_HEIGHT)
self.document.setFont("Helvetica", 10)
self.document.drawRightString(INDENTED_X + 445, self.y, "Acompte")
self.document.drawRightString(INDENTED_RIGHT_X, self.y, str(contract.advance))
self.newline()
self.document.setFont("Helvetica-Bold", 10)
self.document.drawRightString(INDENTED_X + 445, self.y, "Solde à payer")
self.amount = total - contract.advance
self.document.drawRightString(INDENTED_RIGHT_X, self.y, str(self.amount))
def drawColoredRow(self):
"""
Génère une ligne colorée.
"""
self.document.setFillColorCMYK(0.43, 0.2, 0, 0)
self.document.rect(
X, self.y - 4, RIGHT_X - X, COMMON_LINE_HEIGHT, fill=True, stroke=False
)
self.document.setFillColorCMYK(0, 0, 0, 1)
def drawHeaderPrestationsTable(self):
"""
Génère le header de la table des prestations.
"""
self.drawColoredRow()
self.newline()
self.document.setFillColorCMYK(0, 0, 0, 1)
self.drawString(INDENTED_X, "Date")
self.drawString(PRESTATION_COLUMN_2, "Libellé")
self.drawString(INDENTED_X + 365, "Nbre")
self.drawString(INDENTED_X + 420, "Prix Unit.")
self.document.setFont("Helvetica-Bold", 10)
self.document.drawRightString(INDENTED_X + 510, self.y, "Total")
def drawFooterPrestationTable(self, total):
"""
Génère le footer de la table des prestations.
"""
self.drawColoredRow()
self.newline()
self.document.setFont("Helvetica-Bold", 10)
self.document.drawRightString(INDENTED_X + 445, self.y, "Total")
self.document.drawRightString(INDENTED_RIGHT_X, self.y, str(total))
def displayPrestation(self, prestation, total):
"""
Affiche une ligne de prestations dans le tableau.
"""
total += prestation.total_amount
self.drawNewLine(INDENTED_X, str(prestation.date))
self.drawString(PRESTATION_COLUMN_2, prestation.label)
self.document.drawRightString(PRESTATION_COLUMN_3, self.y, str(prestation.unit))
self.document.drawRightString(
PRESTATION_COLUMN_4, self.y, str(prestation.unit_price)
)
self.document.drawRightString(
INDENTED_RIGHT_X, self.y, str(prestation.total_amount)
)
return total
def drawPrestationsTable(self, prestation_list):
"""
Génère le tableau des prestations.
"""
self.drawHeaderPrestationsTable()
total = 0
for prestation in prestation_list:
total = self.displayPrestation(prestation, total)
self.drawFooterPrestationTable(total)
return total
def conclusion(self, info, contract):
"""
Affiche la conclusion de la facture.
"""
self.newline(DOUBLE_LINE_HEIGHT)
self.document.rect(X, self.y, RIGHT_X - X, BIG_LINE_HEIGHT, fill=0)
self.drawNewLine(INDENTED_X, "Merci de bien vouloir payer la somme de ")
self.drawString(INDENTED_X + 184, str(self.amount), font_decoration="Bold")
self.drawString(INDENTED_X + 215, "€ sur le compte ")
self.drawString(INDENTED_X + 290, info["IBAN"], font_decoration="Bold")
self.newline(COMMON_LINE_HEIGHT)
if not contract.is_paid:
the_date = datetime.now()
pay_date = the_date + timedelta(days=15)
self.drawString(INDENTED_X, "Pour le ")
self.drawString(
INDENTED_X + 35,
str(pay_date.day)
+ "/"
+ str(pay_date.month)
+ "/"
+ str(pay_date.year),
font_decoration="Bold",
)
self.drawString(INDENTED_X + 85, " au plus tard, avec la référence :")
self.drawString(
INDENTED_X + 230,
'"' + str(contract.reference) + '"',
font_decoration="Bold",
)
def addSignature(self):
"""
Génère la signature.
"""
self.newline(BIG_LINE_HEIGHT)
self.document.drawString(
INDENTED_X, self.y, "Hennuyères, le " + date.today().strftime("%d-%m-%Y")
)
self.document.drawRightString(RIGHT_X, self.y, "Président")
url = os.path.join(settings.STATICFILES_DIRS[0], "img/signature.png")
self.newline(DOUBLE_LINE_HEIGHT)
self.document.drawImage(url, INDENTED_X + 340, self.y, width=180, height=39)
def footer(self):
"""
Ajoute les conditions générales de payement au document.
"""
self.y = 175
self.newline(DOUBLE_LINE_HEIGHT)
self.drawNewLine(
INDENTED_X, "CONDITIONS GENERALES DE PAIEMENT", font_decoration="Bold"
)
self.drawNewLine(INDENTED_X, "Facture payable au comptant.")
self.drawNewLine(
INDENTED_X,
"En cas de défaut de paiement à l'échéance, il est dû de plein droit et sans mise en demeure, un interêt fixé au taux de",
)
self.drawNewLine(INDENTED_X, "15% l'an.")
self.drawNewLine(
INDENTED_X,
"Tout réclamation, pour être admise, doit être faite dans les huit jours de la réception de la facture.",
)
self.drawNewLine(
INDENTED_X,
"En cas de litige concernant la présente facture, seuls les tribunaux de MONS seront compétents.",
)
def download(self):
# Close the PDF object cleanly, and we're done.
self.document.showPage()
self.document.save()
def contract_export(request, contractid):
"""
Génere une fichier PDF pour fournir au client.
"""
info = loadBillingConfig(request)
contract = Contract.objects.get(pk=contractid)
# Create the HttpResponse object with the appropriate PDF headers.
response = HttpResponse(content_type="application/pdf")
response["Content-Disposition"] = (
'attachment; filename="facture_' + str(contract.reference) + '.pdf"'
)
document = MyDocument(response)
document.header(info, contract)
document.title(contract)
document.payementInformation(info, contract)
document.prestations(contract)
document.conclusion(info, contract)
document.addSignature()
document.footer()
document.download()
return response