Skip to content

Commit 11fb8cf

Browse files
committed
Add easybill csv export
1 parent c772ca7 commit 11fb8cf

4 files changed

Lines changed: 200 additions & 3 deletions

File tree

bakeup/templates/workshop/order_list.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
</a>
1919
<ul class="dropdown-menu">
2020
<li><a class="dropdown-item" href="{% export_url "csv" %}">{% trans "Export as CSV" %}</a></li>
21-
<li><a class="dropdown-item" href="{% url 'workshop:order-export-billbee' %}?{{ request.GET.urlencode }}">{% trans "Export billbee" %}</a></li>
21+
<li><a class="dropdown-item" href="{% url 'workshop:order-export-easybill' %}?{{ request.GET.urlencode }}">{% trans "Export easybill" %}</a></li>
2222
</ul>
2323
</div>
2424
</div>
@@ -39,6 +39,9 @@
3939
<div class="col-2">
4040
{% bootstrap_field filter.form.point_of_sale size='sm' wrapper_class='' layout='horizontal' show_label=False horizontal_field_class='col-12' %}
4141
</div>
42+
<div class="col-2">
43+
{% bootstrap_field filter.form.month size='sm' wrapper_class='' layout='horizontal' show_label=False horizontal_field_class='col-12' %}
44+
</div>
4245
<div class="col-2">
4346
{% bootstrap_field filter.form.production_day size='sm' wrapper_class='' layout='horizontal' show_label=False horizontal_field_class='col-12' %}
4447
</div>

bakeup/workshop/exports.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from collections import defaultdict
2+
13
from django.db.models import Q
24
from django.utils import numberformat
35
from django.utils.timezone import now
@@ -452,3 +454,154 @@ def get_data(self):
452454
@property
453455
def export_name(self):
454456
return "billbee-bestellungen-{}".format(now().strftime("%d-%m-%Y"))
457+
458+
459+
class CustomerOrderEasyBillExportView(StaffPermissionsMixin, ExportMixin, FilterView):
460+
filterset_class = CustomerOrderFilter
461+
model = CustomerOrder
462+
delimiter = ";"
463+
464+
def get_headers(self):
465+
headers = [
466+
"order_number",
467+
"item_type",
468+
"purchase_date",
469+
"currency",
470+
"order_shipping_price",
471+
"payment_type",
472+
"payment_reference",
473+
"customer_number",
474+
"email",
475+
"phone_number",
476+
"firstname",
477+
"lastname",
478+
"street",
479+
"zipcode",
480+
"city",
481+
"state",
482+
"country",
483+
"sku",
484+
"title",
485+
"quantity",
486+
"item_price",
487+
"vat_percent",
488+
"tax_type",
489+
"shipping_date",
490+
"shipping_type",
491+
"payment_date",
492+
"store_id",
493+
]
494+
return headers
495+
496+
def get_data(self):
497+
customer_orders = (
498+
self.object_list.select_related("customer", "customer__user")
499+
.prefetch_related("positions", "positions__product")
500+
.order_by("customer", "created")
501+
)
502+
503+
# Group orders by customer
504+
customer_groups = defaultdict(list)
505+
for order in customer_orders:
506+
customer_groups[order.customer_id].append(order)
507+
508+
rows = []
509+
510+
for customer_id, orders in customer_groups.items():
511+
# Get the first order for the order number and customer data
512+
first_order = min(orders, key=lambda x: x.created)
513+
order_number = first_order.id
514+
515+
# Collect all positions from all orders of this customer
516+
for order in orders:
517+
for position in order.positions.all():
518+
# Format price
519+
item_price = ""
520+
if position.price:
521+
item_price = numberformat.format(
522+
position.price.amount, ".", decimal_pos=4, use_l10n=False
523+
)
524+
525+
# Determine VAT percentage (default to 7%, adjust as needed)
526+
vat_percent = "7.0000"
527+
if hasattr(position.product, "vat_rate"):
528+
vat_percent = numberformat.format(
529+
position.product.vat_rate,
530+
".",
531+
decimal_pos=4,
532+
use_l10n=False,
533+
)
534+
535+
# Format dates
536+
purchase_date = first_order.created.strftime("%Y-%m-%d")
537+
shipping_date = ""
538+
payment_date = ""
539+
540+
if hasattr(order, "shipped_date") and order.shipped_date:
541+
shipping_date = order.shipped_date.strftime("%Y-%m-%d")
542+
543+
if hasattr(order, "paid_date") and order.paid_date:
544+
payment_date = order.paid_date.strftime("%Y-%m-%d")
545+
546+
row = [
547+
str(order_number),
548+
"item",
549+
purchase_date,
550+
"EUR",
551+
"0.00", # order_shipping_price - adjust if you have shipping costs
552+
"", # payment_type - add if available
553+
"",
554+
( # payment_reference - add if available
555+
str(first_order.customer.id) if first_order.customer else ""
556+
),
557+
(
558+
first_order.customer.user.email
559+
if first_order.customer and first_order.customer.user
560+
else ""
561+
),
562+
(
563+
first_order.customer.telephone_number
564+
if first_order.customer
565+
else ""
566+
),
567+
(
568+
first_order.customer.user.first_name
569+
if first_order.customer and first_order.customer.user
570+
else ""
571+
),
572+
(
573+
first_order.customer.user.last_name
574+
if first_order.customer and first_order.customer.user
575+
else ""
576+
),
577+
(
578+
first_order.customer.address_line
579+
if first_order.customer
580+
else ""
581+
),
582+
(
583+
first_order.customer.postal_code
584+
if first_order.customer
585+
else ""
586+
),
587+
first_order.customer.city if first_order.customer else "",
588+
"", # state
589+
"DE",
590+
str(position.product.pk) if position.product else "",
591+
position.product.name if position.product else "", # country
592+
str(position.quantity),
593+
item_price,
594+
vat_percent,
595+
"", # tax_type
596+
shipping_date,
597+
"", # shipping_type - add if available (e.g., "DHL")
598+
payment_date,
599+
"", # store_id - add if you have multiple stores
600+
]
601+
rows.append(row)
602+
603+
return rows
604+
605+
@property
606+
def export_name(self):
607+
return "easybill-orders-{}".format(now().strftime("%Y-%m-%d"))

bakeup/workshop/tables.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
from django.conf import settings
55
from django.contrib.auth.models import Group
66
from django.db.models import Q
7-
from django.db.models.functions import Lower
7+
from django.db.models.functions import Lower, TruncMonth
8+
from django.utils import formats
89
from django.utils.translation import gettext_lazy as _
910
from django_tables2.utils import A
1011
from taggit.models import Tag
@@ -110,13 +111,47 @@ class CustomerOrderFilter(django_filters.FilterSet):
110111
point_of_sale = django_filters.ModelChoiceFilter(
111112
queryset=PointOfSale.objects.all(), empty_label=_("Select a point of sale")
112113
)
114+
month = django_filters.ChoiceFilter(
115+
method="filter_by_month",
116+
label="Monat",
117+
empty_label="Monat auswählen",
118+
)
113119
search = django_filters.filters.CharFilter(
114120
method="filter_search", label=_("Search")
115121
)
116122

117123
class Meta:
118124
model = CustomerOrder
119-
fields = ("production_day", "point_of_sale", "customer")
125+
fields = ("production_day", "point_of_sale", "customer", "month")
126+
127+
def __init__(self, *args, **kwargs):
128+
super().__init__(*args, **kwargs)
129+
# Get distinct year-month combinations from production days with orders
130+
months = (
131+
ProductionDay.objects.filter(customer_orders__isnull=False)
132+
.annotate(month=TruncMonth("day_of_sale"))
133+
.values_list("month", flat=True)
134+
.distinct()
135+
.order_by("-month")
136+
)
137+
138+
# Format choices as (YYYY-MM, Month Year)
139+
month_choices = [
140+
(month.strftime("%Y-%m"), formats.date_format(month, "F Y"))
141+
for month in months
142+
if month
143+
]
144+
145+
self.filters["month"].extra["choices"] = month_choices
146+
147+
def filter_by_month(self, queryset, name, value):
148+
if value:
149+
year, month = value.split("-")
150+
return queryset.filter(
151+
production_day__day_of_sale__year=year,
152+
production_day__day_of_sale__month=month,
153+
)
154+
return queryset
120155

121156
def filter_search(self, queryset, name, value):
122157
return queryset.filter(

bakeup/workshop/urls.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from bakeup.workshop.exports import (
55
CustomerBillbeeExportView,
66
CustomerOrderBillbeeExportView,
7+
CustomerOrderEasyBillExportView,
78
CustomerReady2OrderExportView,
89
CustomerSevdeskExportView,
910
ProductionDayExportView,
@@ -300,6 +301,11 @@
300301
view=CustomerOrderBillbeeExportView.as_view(),
301302
name="order-export-billbee",
302303
),
304+
path(
305+
"orders/export/easybill/",
306+
view=CustomerOrderEasyBillExportView.as_view(),
307+
name="order-export-easybill",
308+
),
303309
path(
304310
"orders/batch/<int:pk>/",
305311
view=CustomerOrderBatchView.as_view(),

0 commit comments

Comments
 (0)