2022-09-16 17:16:27 +02:00
|
|
|
# lingo - payment and billing system
|
|
|
|
# Copyright (C) 2022 Entr'ouvert
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
|
|
# under the terms of the GNU Affero General Public License as published
|
|
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2022-09-21 11:52:51 +02:00
|
|
|
import copy
|
|
|
|
|
2022-09-16 17:16:27 +02:00
|
|
|
from django.contrib.auth.models import Group
|
2022-11-22 08:51:11 +01:00
|
|
|
from django.contrib.postgres.fields import JSONField
|
2022-09-16 17:16:27 +02:00
|
|
|
from django.db import models
|
2022-12-02 16:02:30 +01:00
|
|
|
from django.utils.formats import date_format
|
2022-09-16 17:16:27 +02:00
|
|
|
from django.utils.text import slugify
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
|
|
|
|
from lingo.utils.misc import generate_slug
|
|
|
|
|
|
|
|
|
2022-09-21 11:52:51 +02:00
|
|
|
class RegieImportError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2022-09-16 17:16:27 +02:00
|
|
|
class Regie(models.Model):
|
|
|
|
label = models.CharField(_('Label'), max_length=150)
|
|
|
|
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
|
|
|
description = models.TextField(
|
|
|
|
_('Description'), null=True, blank=True, help_text=_('Optional regie description.')
|
|
|
|
)
|
|
|
|
cashier_role = models.ForeignKey(
|
|
|
|
Group,
|
|
|
|
blank=True,
|
|
|
|
null=True,
|
|
|
|
default=None,
|
|
|
|
related_name='+',
|
|
|
|
verbose_name=_('Cashier Role'),
|
|
|
|
on_delete=models.SET_NULL,
|
|
|
|
)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
ordering = ['label']
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.label
|
|
|
|
|
|
|
|
def save(self, *args, **kwargs):
|
|
|
|
if not self.slug:
|
|
|
|
self.slug = generate_slug(self)
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def base_slug(self):
|
|
|
|
return slugify(self.label)
|
2022-09-21 11:52:51 +02:00
|
|
|
|
|
|
|
def export_json(self):
|
|
|
|
return {
|
|
|
|
'label': self.label,
|
|
|
|
'slug': self.slug,
|
|
|
|
'description': self.description,
|
|
|
|
'permissions': {
|
|
|
|
'cashier': self.cashier_role.name if self.cashier_role else None,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def import_json(cls, data):
|
|
|
|
data = copy.deepcopy(data)
|
|
|
|
permissions = data.pop('permissions') or {}
|
|
|
|
role_name = permissions.get('cashier')
|
|
|
|
if role_name:
|
|
|
|
try:
|
|
|
|
data['cashier_role'] = Group.objects.get(name=role_name)
|
|
|
|
except Group.DoesNotExists:
|
|
|
|
raise RegieImportError('Missing role: %s' % role_name)
|
|
|
|
except Group.MultipleObjectsReturned:
|
|
|
|
raise RegieImportError('Multiple role exist with the name: %s' % role_name)
|
|
|
|
|
|
|
|
regie, created = cls.objects.update_or_create(slug=data['slug'], defaults=data)
|
|
|
|
return created, regie
|
2022-11-22 08:51:11 +01:00
|
|
|
|
|
|
|
|
2022-12-01 16:44:25 +01:00
|
|
|
class Campaign(models.Model):
|
|
|
|
date_start = models.DateField(_('Start date'))
|
|
|
|
date_end = models.DateField(_('End date'))
|
|
|
|
date_issue = models.DateField(_('Issue date'))
|
|
|
|
|
2022-12-02 16:02:30 +01:00
|
|
|
def __str__(self):
|
|
|
|
return _('From %(start)s to %(end)s') % {
|
|
|
|
'start': date_format(self.date_start, 'd/m/Y'),
|
|
|
|
'end': date_format(self.date_end, 'd/m/Y'),
|
|
|
|
}
|
|
|
|
|
2022-12-01 16:44:25 +01:00
|
|
|
|
|
|
|
class Pool(models.Model):
|
|
|
|
campaign = models.ForeignKey(Campaign, on_delete=models.PROTECT)
|
|
|
|
draft = models.BooleanField(default=True)
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
|
|
|
|
|
2022-11-22 08:51:11 +01:00
|
|
|
class AbstractInvoice(models.Model):
|
|
|
|
label = models.CharField(_('Label'), max_length=300)
|
2022-11-28 16:41:29 +01:00
|
|
|
total_amount = models.DecimalField(max_digits=9, decimal_places=2, default=0)
|
2022-11-22 08:51:11 +01:00
|
|
|
date_issue = models.DateField(_('Issue date'))
|
|
|
|
regie = models.ForeignKey(Regie, on_delete=models.PROTECT)
|
|
|
|
payer = models.CharField(_('Payer'), max_length=300)
|
|
|
|
|
2022-12-01 16:44:25 +01:00
|
|
|
pool = models.ForeignKey(Pool, on_delete=models.PROTECT)
|
|
|
|
|
2022-11-22 08:51:11 +01:00
|
|
|
class Meta:
|
|
|
|
abstract = True
|
|
|
|
|
|
|
|
|
|
|
|
class DraftInvoice(AbstractInvoice):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Invoice(AbstractInvoice):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class AbstractInvoiceLine(models.Model):
|
|
|
|
slug = models.SlugField(max_length=250)
|
|
|
|
label = models.CharField(max_length=260)
|
|
|
|
quantity = models.FloatField()
|
|
|
|
unit_amount = models.DecimalField(max_digits=9, decimal_places=2)
|
|
|
|
total_amount = models.DecimalField(max_digits=9, decimal_places=2)
|
|
|
|
|
|
|
|
user_external_id = models.CharField(max_length=250)
|
|
|
|
payer_external_id = models.CharField(max_length=250)
|
|
|
|
event = JSONField(default=dict)
|
|
|
|
pricing_data = JSONField(default=dict)
|
|
|
|
status = models.CharField(
|
|
|
|
max_length=10,
|
|
|
|
choices=[
|
|
|
|
('success', _('Success')),
|
|
|
|
('error', _('Error')),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2022-12-01 16:44:25 +01:00
|
|
|
pool = models.ForeignKey(Pool, on_delete=models.PROTECT)
|
|
|
|
|
2022-11-22 08:51:11 +01:00
|
|
|
class Meta:
|
|
|
|
abstract = True
|
|
|
|
|
|
|
|
|
|
|
|
class DraftInvoiceLine(AbstractInvoiceLine):
|
|
|
|
invoice = models.ForeignKey(DraftInvoice, on_delete=models.PROTECT, null=True, related_name='lines')
|
|
|
|
|
|
|
|
|
|
|
|
class InvoiceLine(AbstractInvoiceLine):
|
|
|
|
invoice = models.ForeignKey(Invoice, on_delete=models.PROTECT, null=True, related_name='lines')
|