mirror of
https://github.com/pretix/pretix.git
synced 2026-08-18 12:06:26 +00:00
Add new fields to invoice model and API
This commit is contained in:
@@ -45,6 +45,14 @@ class CompatibleCountryField(serializers.Field):
|
||||
return instance.country_old
|
||||
|
||||
|
||||
class CountryField(serializers.Field):
|
||||
def to_internal_value(self, data):
|
||||
return {self.field_name: Country(data)}
|
||||
|
||||
def to_representation(self, src):
|
||||
return str(src) if src else None
|
||||
|
||||
|
||||
class InvoiceAddressSerializer(I18nAwareModelSerializer):
|
||||
country = CompatibleCountryField(source='*')
|
||||
name = serializers.CharField(required=False)
|
||||
@@ -1322,17 +1330,24 @@ class InlineInvoiceLineSerializer(I18nAwareModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = InvoiceLine
|
||||
fields = ('position', 'description', 'gross_value', 'tax_value', 'tax_rate', 'tax_name')
|
||||
fields = ('position', 'description', 'item', 'variation', 'attendee_name', 'event_date_from',
|
||||
'event_date_to', 'gross_value', 'tax_value', 'tax_rate', 'tax_name')
|
||||
|
||||
|
||||
class InvoiceSerializer(I18nAwareModelSerializer):
|
||||
order = serializers.SlugRelatedField(slug_field='code', read_only=True)
|
||||
refers = serializers.SlugRelatedField(slug_field='invoice_no', read_only=True)
|
||||
refers = serializers.SlugRelatedField(slug_field='full_invoice_no', read_only=True)
|
||||
lines = InlineInvoiceLineSerializer(many=True)
|
||||
invoice_to_country = CountryField()
|
||||
invoice_from_country = CountryField()
|
||||
|
||||
class Meta:
|
||||
model = Invoice
|
||||
fields = ('order', 'number', 'is_cancellation', 'invoice_from', 'invoice_to', 'date', 'refers', 'locale',
|
||||
fields = ('order', 'number', 'is_cancellation', 'invoice_from', 'invoice_from_name', 'invoice_from_zipcode',
|
||||
'invoice_from_city', 'invoice_from_country', 'invoice_from_tax_id', 'invoice_from_vat_id',
|
||||
'invoice_to', 'invoice_to_company', 'invoice_to_name', 'invoice_to_street', 'invoice_to_zipcode',
|
||||
'invoice_to_city', 'invoice_to_state', 'invoice_to_country', 'invoice_to_vat_id', 'invoice_to_beneficiary',
|
||||
'custom_field', 'date', 'refers', 'locale',
|
||||
'introductory_text', 'additional_text', 'payment_provider_text', 'footer_text', 'lines',
|
||||
'foreign_currency_display', 'foreign_currency_rate', 'foreign_currency_rate_date',
|
||||
'internal_reference')
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Generated by Django 3.0.12 on 2021-03-08 13:26
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0177_auto_20210301_1510'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='invoiceline',
|
||||
name='attendee_name',
|
||||
field=models.TextField(null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoiceline',
|
||||
name='event_date_to',
|
||||
field=models.DateTimeField(null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoiceline',
|
||||
name='item',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.Item'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoiceline',
|
||||
name='variation',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.ItemVariation'),
|
||||
),
|
||||
]
|
||||
@@ -273,6 +273,14 @@ class InvoiceLine(models.Model):
|
||||
:type subevent: SubEvent
|
||||
:param event_date_from: Event date of the (sub)event at the time the invoice was created
|
||||
:type event_date_from: datetime
|
||||
:param event_date_to: Event end date of the (sub)event at the time the invoice was created
|
||||
:type event_date_to: datetime
|
||||
:param item: The item this line refers to
|
||||
:type item: Item
|
||||
:param variation: The variation this line refers to
|
||||
:type variation: ItemVariation
|
||||
:param attendee_name: The attendee name at the time the invoice was created
|
||||
:type attendee_name: str
|
||||
"""
|
||||
invoice = models.ForeignKey('Invoice', related_name='lines', on_delete=models.CASCADE)
|
||||
position = models.PositiveIntegerField(default=0)
|
||||
@@ -283,6 +291,10 @@ class InvoiceLine(models.Model):
|
||||
tax_name = models.CharField(max_length=190)
|
||||
subevent = models.ForeignKey('SubEvent', null=True, blank=True, on_delete=models.PROTECT)
|
||||
event_date_from = models.DateTimeField(null=True)
|
||||
event_date_to = models.DateTimeField(null=True)
|
||||
item = models.ForeignKey('Item', null=True, blank=True, on_delete=models.PROTECT)
|
||||
variation = models.ForeignKey('ItemVariation', null=True, blank=True, on_delete=models.PROTECT)
|
||||
attendee_name = models.TextField(null=True, blank=True)
|
||||
|
||||
@property
|
||||
def net_value(self):
|
||||
|
||||
@@ -171,9 +171,17 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
if invoice.event.has_subevents:
|
||||
desc += "<br />" + pgettext("subevent", "Date: {}").format(p.subevent)
|
||||
InvoiceLine.objects.create(
|
||||
position=i, invoice=invoice, description=desc,
|
||||
gross_value=p.price, tax_value=p.tax_value,
|
||||
subevent=p.subevent, event_date_from=(p.subevent.date_from if p.subevent else invoice.event.date_from),
|
||||
position=i,
|
||||
invoice=invoice,
|
||||
description=desc,
|
||||
gross_value=p.price,
|
||||
tax_value=p.tax_value,
|
||||
subevent=p.subevent,
|
||||
item=p.item,
|
||||
variation=p.variation,
|
||||
attendee_name=p.attendee_name if invoice.event.settings.invoice_attendee_name else None,
|
||||
event_date_from=p.subevent.date_from if invoice.event.has_subevents else invoice.event.date_from,
|
||||
event_date_to=p.subevent.date_to if invoice.event.has_subevents else invoice.event.date_to,
|
||||
tax_rate=p.tax_rate, tax_name=p.tax_rule.name if p.tax_rule else ''
|
||||
)
|
||||
|
||||
@@ -198,6 +206,8 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
invoice=invoice,
|
||||
description=fee_title,
|
||||
gross_value=fee.value,
|
||||
event_date_from=None if invoice.event.has_subevents else invoice.event.date_from,
|
||||
event_date_to=None if invoice.event.has_subevents else invoice.event.date_to,
|
||||
tax_value=fee.tax_value,
|
||||
tax_rate=fee.tax_rate,
|
||||
tax_name=fee.tax_rule.name if fee.tax_rule else ''
|
||||
|
||||
@@ -5,7 +5,9 @@ from urllib.parse import urlencode
|
||||
from django import forms
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.db.models import Exists, F, Max, Model, OuterRef, Q, QuerySet, Count
|
||||
from django.db.models import (
|
||||
Count, Exists, F, Max, Model, OuterRef, Q, QuerySet,
|
||||
)
|
||||
from django.db.models.functions import Coalesce, ExtractWeekDay
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.formats import date_format, localize
|
||||
|
||||
@@ -960,8 +960,24 @@ TEST_INVOICE_RES = {
|
||||
"order": "FOO",
|
||||
"number": "DUMMY-00001",
|
||||
"is_cancellation": False,
|
||||
"invoice_from_name": "",
|
||||
"invoice_from": "",
|
||||
"invoice_from_zipcode": "",
|
||||
"invoice_from_city": "",
|
||||
"invoice_from_country": None,
|
||||
"invoice_from_tax_id": "",
|
||||
"invoice_from_vat_id": "",
|
||||
"invoice_to": "Sample company\nNew Zealand\nVAT-ID: DE123",
|
||||
"invoice_to_company": "Sample company",
|
||||
"invoice_to_name": "",
|
||||
"invoice_to_street": "",
|
||||
"invoice_to_zipcode": "",
|
||||
"invoice_to_city": "",
|
||||
"invoice_to_state": "",
|
||||
"invoice_to_country": "NZ",
|
||||
"invoice_to_vat_id": "DE123",
|
||||
"invoice_to_beneficiary": "",
|
||||
"custom_field": None,
|
||||
"date": "2017-12-10",
|
||||
"refers": None,
|
||||
"locale": "en",
|
||||
@@ -977,6 +993,11 @@ TEST_INVOICE_RES = {
|
||||
{
|
||||
"position": 1,
|
||||
"description": "Budget Ticket<br />Attendee: Peter",
|
||||
'event_date_from': '2017-12-27T10:00:00Z',
|
||||
'event_date_to': None,
|
||||
'attendee_name': 'Peter',
|
||||
'item': None,
|
||||
'variation': None,
|
||||
"gross_value": "23.00",
|
||||
"tax_value": "0.00",
|
||||
"tax_name": "",
|
||||
@@ -985,6 +1006,11 @@ TEST_INVOICE_RES = {
|
||||
{
|
||||
"position": 2,
|
||||
"description": "Payment fee",
|
||||
'event_date_from': '2017-12-27T10:00:00Z',
|
||||
'event_date_to': None,
|
||||
'attendee_name': None,
|
||||
'item': None,
|
||||
'variation': None,
|
||||
"gross_value": "0.25",
|
||||
"tax_value": "0.05",
|
||||
"tax_name": "",
|
||||
@@ -995,8 +1021,9 @@ TEST_INVOICE_RES = {
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_invoice_list(token_client, organizer, event, order, invoice):
|
||||
def test_invoice_list(token_client, organizer, event, order, item, invoice):
|
||||
res = dict(TEST_INVOICE_RES)
|
||||
res['lines'][0]['item'] = item.pk
|
||||
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/'.format(organizer.slug, event.slug))
|
||||
assert resp.status_code == 200
|
||||
@@ -1043,8 +1070,9 @@ def test_invoice_list(token_client, organizer, event, order, invoice):
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_invoice_detail(token_client, organizer, event, invoice):
|
||||
def test_invoice_detail(token_client, organizer, event, item, invoice):
|
||||
res = dict(TEST_INVOICE_RES)
|
||||
res['lines'][0]['item'] = item.pk
|
||||
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/invoices/{}/'.format(organizer.slug, event.slug,
|
||||
invoice.number))
|
||||
@@ -4300,12 +4328,30 @@ def test_order_create_invoice(token_client, organizer, event, order):
|
||||
), format='json', data={}
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
assert resp.data == {
|
||||
with scopes_disabled():
|
||||
pos = order.positions.first()
|
||||
assert json.loads(json.dumps(resp.data)) == {
|
||||
'order': 'FOO',
|
||||
'number': 'DUMMY-00001',
|
||||
'is_cancellation': False,
|
||||
'invoice_from': '',
|
||||
'invoice_to': 'Sample company\nNew Zealand\nVAT-ID: DE123',
|
||||
"invoice_from_name": "",
|
||||
"invoice_from": "",
|
||||
"invoice_from_zipcode": "",
|
||||
"invoice_from_city": "",
|
||||
"invoice_from_country": None,
|
||||
"invoice_from_tax_id": "",
|
||||
"invoice_from_vat_id": "",
|
||||
"invoice_to": "Sample company\nNew Zealand\nVAT-ID: DE123",
|
||||
"invoice_to_company": "Sample company",
|
||||
"invoice_to_name": "",
|
||||
"invoice_to_street": "",
|
||||
"invoice_to_zipcode": "",
|
||||
"invoice_to_city": "",
|
||||
"invoice_to_state": "",
|
||||
"invoice_to_country": "NZ",
|
||||
"invoice_to_vat_id": "DE123",
|
||||
"invoice_to_beneficiary": "",
|
||||
"custom_field": None,
|
||||
'date': now().date().isoformat(),
|
||||
'refers': None,
|
||||
'locale': 'en',
|
||||
@@ -4317,6 +4363,11 @@ def test_order_create_invoice(token_client, organizer, event, order):
|
||||
{
|
||||
'position': 1,
|
||||
'description': 'Budget Ticket<br />Attendee: Peter',
|
||||
'event_date_from': '2017-12-27T10:00:00Z',
|
||||
'event_date_to': None,
|
||||
'attendee_name': 'Peter',
|
||||
'item': pos.item_id,
|
||||
'variation': None,
|
||||
'gross_value': '23.00',
|
||||
'tax_value': '0.00',
|
||||
'tax_rate': '0.00',
|
||||
@@ -4325,6 +4376,11 @@ def test_order_create_invoice(token_client, organizer, event, order):
|
||||
{
|
||||
'position': 2,
|
||||
'description': 'Payment fee',
|
||||
'event_date_from': '2017-12-27T10:00:00Z',
|
||||
'event_date_to': None,
|
||||
'attendee_name': None,
|
||||
'item': None,
|
||||
'variation': None,
|
||||
'gross_value': '0.25',
|
||||
'tax_value': '0.05',
|
||||
'tax_rate': '19.00',
|
||||
|
||||
Reference in New Issue
Block a user