Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
mhieta committed Nov 14, 2024
2 parents d425ff9 + ee231f2 commit 18ec4dd
Show file tree
Hide file tree
Showing 37 changed files with 2,157 additions and 287 deletions.
8 changes: 8 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,12 @@ TALPA_ORDER_PAYMENT_MAX_PERIOD_MINS=
TALPA_ORDER_PAYMENT_WEBHOOK_WAIT_BUFFER_MINS=
TALPA_WEBHOOK_WAIT_BUFFER_SECONDS=
TALPA_SUBSCRIPTION_PERIOD_UNIT=
TALPA_DEFAULT_ACCOUNTING_COMPANY_CODE=
TALPA_DEFAULT_ACCOUNTING_VAT_CODE=
TALPA_DEFAULT_ACCOUNTING_INTERNAL_ORDER=
TALPA_DEFAULT_ACCOUNTING_PROFIT_CENTER=
TALPA_DEFAULT_ACCOUNTING_BALANCE_PROFIT_CENTER=
TALPA_DEFAULT_ACCOUNTING_PROJECT=
TALPA_DEFAULT_ACCOUNTING_OPERATION_AREA=
TALPA_DEFAULT_ACCOUNTING_MAIN_LEDGER_ACCOUNT=

24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.3.0] - 2024-11-14

### Added

- Accounting support for products ([7801d6d](https://github.com/City-of-Helsinki/parking-permits/commit/7801d6dad2a539b766b98b4e18855137308eb3b8))
- Update OrderAdmin to display all distinct VATs ([eafcc58](https://github.com/City-of-Helsinki/parking-permits/commit/eafcc588f764ef4356accb9d4a56a17792e6cdd6))
- Add DVV-checks for Talpa rights of purchase (disabled for now) ([11a1f74](https://github.com/City-of-Helsinki/parking-permits/commit/11a1f74c49dd09907f68e72a3d8600a25ed4ec2a))

### Changed

- Refactor Refund creation to own utility ([0c2d613](https://github.com/City-of-Helsinki/parking-permits/commit/0c2d61311f307ccce34eab8138a375c2dc9b8fe1))
- Calculate vehicle change refunds as VAT-based ([4c221c7](https://github.com/City-of-Helsinki/parking-permits/commit/4c221c77e42239b128325c00732f4a5064f801c9))
- Update refund order linking and vats ([8d0275f](https://github.com/City-of-Helsinki/parking-permits/commit/8d0275fb3ef1fdd62ecb7d4686d3e24b6653ee9e))
- Update address change functionality ([776f0cc](https://github.com/City-of-Helsinki/parking-permits/commit/776f0ccfe5242e31557cc108d4ec71f13c41de07))
- Update VAT-based refund calculation ([2c90a05](https://github.com/City-of-Helsinki/parking-permits/commit/2c90a05d4127f25354c14e63c211861cc21422d6))
- Update refund to support multiple orders ([d33af03](https://github.com/City-of-Helsinki/parking-permits/commit/d33af03538d1cd78a070e8d987e1e0ea3482f26f))
- Update unused items calculation ([be7e7c2](https://github.com/City-of-Helsinki/parking-permits/commit/be7e7c2582ea3708e47a788b0e69c80e4e700c63))

### Fixed

- Fix end times for open ended permits ([5dc3423](https://github.com/City-of-Helsinki/parking-permits/commit/5dc34237dd6fe228598ae0d11345d7170878d2d6))
- Fix Talpa order renewal vat format ([947b243](https://github.com/City-of-Helsinki/parking-permits/commit/947b2434885d7dc74c7276efa0c18718100f65c6))


## [1.2.4] - 2024-11-04

### Added
Expand Down
43 changes: 35 additions & 8 deletions parking_permits/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
Vehicle,
)
from parking_permits.models.parking_permit import ParkingPermitEvent
from parking_permits.models.product import Accounting
from parking_permits.models.vehicle import VehiclePowerType, VehicleUser


Expand Down Expand Up @@ -205,28 +206,48 @@ class RefundAdmin(admin.ModelAdmin):
list_display = (
"name",
"iban",
"order",
"get_orders",
"amount",
"status",
"created_at",
"accepted_at",
"get_vat_percent",
)
list_select_related = ("order",)
ordering = ("-created_at",)
raw_id_fields = (
"accepted_by",
"created_by",
"modified_by",
"order",
"orders",
"permits",
)

@admin.display(description="Orders")
def get_orders(self, obj):
return ", ".join([str(order.id) for order in obj.orders.all()])

@admin.display(description="VAT percent")
def get_vat_percent(self, obj):
return format(obj.vat_percent, ".2f")


@admin.register(Accounting)
class AccountingAdmin(admin.ModelAdmin):
list_display = (
"id",
"active_from",
"company_code",
"vat_code",
"internal_order",
"profit_center",
"balance_profit_center",
"project",
"operation_area",
"main_ledger_account",
)
ordering = ("-created_at",)


@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = (
Expand All @@ -237,6 +258,8 @@ class ProductAdmin(admin.ModelAdmin):
"unit_price",
"low_emission_discount_percentage",
"talpa_product_id",
"accounting",
"next_accounting",
)
list_select_related = ("zone",)
readonly_fields = ("talpa_product_id",)
Expand All @@ -257,13 +280,13 @@ class OrderAdmin(admin.ModelAdmin):
"address_text",
"parking_zone_name",
"vehicles",
"get_vat_percent",
"get_vat_percents",
)
list_select_related = ("customer",)
readonly_fields = (
"talpa_order_id",
"total_payment_price",
"get_vat_percent",
"get_vat_percents",
)
search_fields = (
"customer__last_name",
Expand All @@ -279,9 +302,13 @@ class OrderAdmin(admin.ModelAdmin):
)
ordering = ("-created_at",)

@admin.display(description="Vat percent")
def get_vat_percent(self, obj):
return format(obj.vat_percent, ".2f") if obj.vat_percent else None
@admin.display(description="Vat percents")
def get_vat_percents(self, obj):
return (
", ".join([format(vat * 100, ".2f") for vat in obj.vat_values])
if obj.vat_values
else None
)


@admin.register(OrderItem)
Expand Down
59 changes: 39 additions & 20 deletions parking_permits/admin_resolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import re
from collections import Counter
from copy import deepcopy
from decimal import Decimal

from ariadne import (
MutationType,
Expand Down Expand Up @@ -81,7 +82,7 @@
)
from .models.refund import RefundStatus
from .models.vehicle import VehiclePowerType
from .resolver_utils import end_permits
from .resolver_utils import create_refund, end_permits
from .services import kami
from .services.dvv import get_person_info
from .services.mail import (
Expand Down Expand Up @@ -792,6 +793,17 @@ def resolve_update_resident_permit(
)
)

# clear vehicle_changed and address_changed flags
if vehicle_changed:
permit.vehicle_changed = False
permit.vehicle_changed_date = None
permit.save()

if address_changed:
permit.address_changed = False
permit.address_changed_date = None
permit.save()

total_price_change_by_order = Counter()
fixed_period_permits = active_permits.fixed_period()
if address_changed and fixed_period_permits.count() > 0:
Expand All @@ -809,27 +821,24 @@ def resolve_update_resident_permit(

customer = update_or_create_customer(customer_info)

for order, order_total_price_change in total_price_change_by_order.items():
if customer_total_price_change < 0:
if customer_total_price_change < 0:
refunds = []
for order, order_total_price_change in total_price_change_by_order.items():
logger.info("Creating refund for current order")
refund = Refund.objects.create(
name=customer.full_name,
order=order,
amount=-customer_total_price_change,
permits = order.permits.all()
refund = create_refund(
user=request.user,
permits=permits,
orders=[order],
amount=Decimal(abs(order_total_price_change)),
iban=iban,
vat=(
order.order_items.first().vat
if order.order_items.exists()
else DEFAULT_VAT
),
description=f"Refund for updating permit: {permit.id}",
vat=(order.vat if order.vat else DEFAULT_VAT),
description=f"Refund for updating permits: {','.join([str(permit.id) for permit in permits])}",
)
refund.permits.add(permit)
logger.info(f"Refund for lowered permit price created: {refund}")
ParkingPermitEventFactory.make_create_refund_event(
permit, refund, created_by=request.user
)
send_refund_email(RefundEmailType.CREATED, customer, [refund])
refunds.append(refund)
if refunds:
send_refund_email(RefundEmailType.CREATED, customer, refunds)

bypass_traficom_validation = permit_info.get("bypass_traficom_validation", False)

Expand Down Expand Up @@ -888,7 +897,7 @@ def resolve_update_resident_permit(
send_vehicle_low_emission_discount_email(
PermitEmailType.VEHICLE_LOW_EMISSION_DISCOUNT_ACTIVATED, permit
)
if permit.is_fixed_period:
if permit.is_fixed_period and customer_total_price_change > 0:
logger.info(f"Creating renewal order for permit: {permit.id}")
new_order = Order.objects.create_renewal_order(
customer,
Expand Down Expand Up @@ -1057,6 +1066,10 @@ def resolve_update_product(obj, info, product_id, product):
_product.modified_by = request.user
_product.save()
_product.create_talpa_product()
_product.update_talpa_accounting()
if _product.accounting:
_product.accounting.modified_by = request.user
_product.accounting.save()
return {"success": True}


Expand Down Expand Up @@ -1090,6 +1103,10 @@ def resolve_create_product(obj, info, product):
modified_by=request.user,
)
product.create_talpa_product()
product.create_talpa_accounting()
product.accounting.created_by = request.user
product.accounting.modified_by = request.user
product.accounting.save()
return {"success": True}


Expand Down Expand Up @@ -1141,7 +1158,9 @@ def resolve_accept_refunds(obj, info, ids):
id__in=ids, status=RefundStatus.ACCEPTED
).select_related("order__customer")
for refund in accepted_refunds:
send_refund_email(RefundEmailType.ACCEPTED, refund.order.customer, [refund])
send_refund_email(
RefundEmailType.ACCEPTED, refund.orders.first().customer, [refund]
)
return qs.count()


Expand Down
3 changes: 2 additions & 1 deletion parking_permits/cron.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ def automatic_expiration_of_permits():
now = tz.localtime(tz.now())
ending_permits = ParkingPermit.objects.filter(
Q(end_time__lt=now)
| Q(vehicle_changed=True, vehicle_changed_date__lt=now.date()),
| Q(vehicle_changed=True, vehicle_changed_date__lt=now.date())
| Q(address_changed=True, address_changed_date__lt=now.date()),
status=ParkingPermitStatus.VALID,
)
for permit in ending_permits:
Expand Down
14 changes: 12 additions & 2 deletions parking_permits/customer_permit.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,16 @@ def get(self):
products.append(product)
permit.products = products

if permit.has_address_changed:
logger.info(
f"Permit {permit.pk} address {permit.address} does not match "
f"with customer's primary or other address. "
f"Marking permit to end at the end of the day."
)
permit.address_changed = True
permit.address_changed_date = tz.localdate()
permit.save()

# automatically cancel permit and it's latest order if payment is not completed in configured time
# (default 15 minutes)
payment_wait_time_buffer = (
Expand All @@ -189,7 +199,7 @@ def get(self):
permit.latest_order.talpa_last_valid_purchase_time
+ tz.timedelta(minutes=payment_wait_time_buffer)
)
< tz.localtime(tz.now())
< tz.localtime()
):
permit.status = CANCELLED
latest_order = permit.latest_order
Expand Down Expand Up @@ -489,7 +499,7 @@ def _is_valid_user_address(self, address_id):
primary = self.customer.primary_address
other = self.customer.other_address

# Check if zone belongs to either of the user address zone
# Check if the address is primary or other address of the user
if primary and primary.id == address_id:
return True
if other and other.id == address_id:
Expand Down
12 changes: 7 additions & 5 deletions parking_permits/exporters.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,10 @@ def get_source_object(self, object_id):

@staticmethod
def get_refund_content(refund):
customer = refund.orders.first().customer
return [
_("Refund ID") + ": " + f"{refund.id}",
_("Customer")
+ ": "
+ f"{refund.order.customer.first_name} {refund.order.customer.last_name}",
_("Customer") + ": " + f"{customer.first_name} {customer.last_name}",
_("Amount")
+ ": "
+ f"{refund.amount} e ({_('incl. VAT')} {_format_percentage(refund.vat_percent)} %)",
Expand Down Expand Up @@ -346,11 +345,14 @@ def set_content(self, obj):
self.set_font("Arial", "B", 12)
self.cell(0, 7, _("Order info"), 0, 1)
self.set_font("Arial", "", 12)
order_content = self.get_order_content(obj.order)
order_content = []
orders = obj.refund_orders
for order in orders:
order_content.extend(self.get_order_content(order))
for line in order_content:
self.cell(0, 7, line, 0, 1)

permits = obj.order.permits.order_by("-primary_vehicle")
permits = obj.refund_permits
for permit in permits:
self.ln(5)
self.set_font("Arial", "B", 12)
Expand Down
9 changes: 5 additions & 4 deletions parking_permits/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ def get_order_fields_mapping(self):
return {
"id": ["id"],
"name": ["name"],
"orderId": ["order_id"],
"registrationNumber": ["order__permits__vehicle__registration_number"],
"orderId": ["orders__id"],
"registrationNumber": ["permits__vehicle__registration_number"],
"accountNumber": ["iban"],
"createdAt": ["created_at"],
"acceptedAt": ["accepted_at"],
Expand All @@ -222,8 +222,9 @@ def filter_queryset(self, qs):
if q:
text_filters = (
Q(name__icontains=q)
| Q(order__permits__vehicle__registration_number__icontains=q)
| Q(permits__vehicle__registration_number__icontains=q)
| Q(iban__icontains=q)
| Q(orders__id__icontains=q)
)
qs = qs.filter(text_filters)
has_filters = True
Expand All @@ -237,7 +238,7 @@ def filter_queryset(self, qs):
qs = qs.filter(status=status)
has_filters = True
if payment_types:
qs = qs.filter(order__payment_type__in=payment_types)
qs = qs.filter(orders__payment_type__in=payment_types)
has_filters = True

return qs if has_filters else self.get_empty_queryset()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 5.0.6 on 2024-09-12 11:56

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("parking_permits", "0060_alter_customer_names"),
]

operations = [
migrations.AddField(
model_name="parkingpermit",
name="address_changed",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="parkingpermit",
name="address_changed_date",
field=models.DateField(
blank=True, null=True, verbose_name="Address changed date"
),
),
]
Loading

0 comments on commit 18ec4dd

Please sign in to comment.