From e34109fd3e52e7666524e229f22e1de52e893210 Mon Sep 17 00:00:00 2001 From: gitea Date: Mon, 17 Feb 2025 11:20:55 +0000 Subject: [PATCH] add history + some fixes --- inventory/migrations/0026_carhistory.py | 29 ++++++++++++ .../migrations/0027_carhistory_dealer.py | 20 ++++++++ .../0028_carhistory_additional_info.py | 18 +++++++ ...rhistory_cost_remove_carhistory_mileage.py | 21 +++++++++ ...ctivity_activity_type_delete_carhistory.py | 21 +++++++++ inventory/models.py | 7 ++- inventory/signals.py | 6 ++- inventory/urls.py | 1 + inventory/utils.py | 10 ++-- inventory/views.py | 8 +++- templates/inventory/car_detail.html | 22 ++++----- templates/inventory/car_history.html | 47 +++++++++++++++++++ templates/sales/estimates/estimate_form.html | 2 + 13 files changed, 192 insertions(+), 20 deletions(-) create mode 100644 inventory/migrations/0026_carhistory.py create mode 100644 inventory/migrations/0027_carhistory_dealer.py create mode 100644 inventory/migrations/0028_carhistory_additional_info.py create mode 100644 inventory/migrations/0029_remove_carhistory_cost_remove_carhistory_mileage.py create mode 100644 inventory/migrations/0030_alter_activity_activity_type_delete_carhistory.py create mode 100644 templates/inventory/car_history.html diff --git a/inventory/migrations/0026_carhistory.py b/inventory/migrations/0026_carhistory.py new file mode 100644 index 00000000..21e89fa1 --- /dev/null +++ b/inventory/migrations/0026_carhistory.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.17 on 2025-02-17 08:54 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0025_email_status'), + ] + + operations = [ + migrations.CreateModel( + name='CarHistory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('event_date', models.DateField()), + ('event_type', models.CharField(choices=[('PURCHASE', 'Purchase'), ('SALE', 'Sale'), ('TRANSFER', 'Transfer'), ('ACCIDENT', 'Accident'), ('MAINTENANCE', 'Maintenance'), ('SERVICE', 'Service'), ('OTHER', 'Other')], max_length=50)), + ('description', models.TextField(blank=True, null=True)), + ('mileage', models.IntegerField(blank=True, null=True)), + ('cost', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), + ('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='history', to='inventory.car')), + ], + options={ + 'ordering': ['-event_date'], + }, + ), + ] diff --git a/inventory/migrations/0027_carhistory_dealer.py b/inventory/migrations/0027_carhistory_dealer.py new file mode 100644 index 00000000..8d5bdb6b --- /dev/null +++ b/inventory/migrations/0027_carhistory_dealer.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.17 on 2025-02-17 08:57 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0026_carhistory'), + ] + + operations = [ + migrations.AddField( + model_name='carhistory', + name='dealer', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='history', to='inventory.dealer'), + preserve_default=False, + ), + ] diff --git a/inventory/migrations/0028_carhistory_additional_info.py b/inventory/migrations/0028_carhistory_additional_info.py new file mode 100644 index 00000000..5c54c367 --- /dev/null +++ b/inventory/migrations/0028_carhistory_additional_info.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.17 on 2025-02-17 09:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0027_carhistory_dealer'), + ] + + operations = [ + migrations.AddField( + model_name='carhistory', + name='additional_info', + field=models.JSONField(blank=True, default=dict, null=True, verbose_name='Car History Additional Info'), + ), + ] diff --git a/inventory/migrations/0029_remove_carhistory_cost_remove_carhistory_mileage.py b/inventory/migrations/0029_remove_carhistory_cost_remove_carhistory_mileage.py new file mode 100644 index 00000000..131fea9b --- /dev/null +++ b/inventory/migrations/0029_remove_carhistory_cost_remove_carhistory_mileage.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.17 on 2025-02-17 09:03 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0028_carhistory_additional_info'), + ] + + operations = [ + migrations.RemoveField( + model_name='carhistory', + name='cost', + ), + migrations.RemoveField( + model_name='carhistory', + name='mileage', + ), + ] diff --git a/inventory/migrations/0030_alter_activity_activity_type_delete_carhistory.py b/inventory/migrations/0030_alter_activity_activity_type_delete_carhistory.py new file mode 100644 index 00000000..6e731bf0 --- /dev/null +++ b/inventory/migrations/0030_alter_activity_activity_type_delete_carhistory.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.17 on 2025-02-17 09:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0029_remove_carhistory_cost_remove_carhistory_mileage'), + ] + + operations = [ + migrations.AlterField( + model_name='activity', + name='activity_type', + field=models.CharField(choices=[('call', 'Call'), ('sms', 'SMS'), ('email', 'Email'), ('whatsapp', 'WhatsApp'), ('visit', 'Visit'), ('add_car', 'Add Car'), ('sale_car', 'Sale Car'), ('reserve_car', 'Reserve Car'), ('transfer_car', 'Transfer Car'), ('remove_car', 'Remove Car'), ('create_quotation', 'Create Quotation'), ('cancel_quotation', 'Cancel Quotation'), ('create_order', 'Create Order'), ('cancel_order', 'Cancel Order'), ('create_invoice', 'Create Invoice'), ('cancel_invoice', 'Cancel Invoice')], max_length=50, verbose_name='Activity Type'), + ), + migrations.DeleteModel( + name='CarHistory', + ), + ] diff --git a/inventory/models.py b/inventory/models.py index 66c3d73d..c0665c5f 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -436,10 +436,11 @@ class Car(models.Model): hash_object.update(f"{self.id_car_make.name}{self.id_car_model.name}{self.year}{self.id_car_serie.name}{self.id_car_trim.name}{color}".encode('utf-8')) return hash_object.hexdigest() - def mark_as_sold(self): + def mark_as_sold(self,user): self.cancel_reservation() - self.status = CarStatusChoices.SOLD + self.status = CarStatusChoices.SOLD self.save() + Activity.objects.create(content_object=self, notes="Car Sold",created_by=user,activity_type=ActionChoices.SALE_CAR) def cancel_reservation(self): if self.reservations.exists(): @@ -996,7 +997,9 @@ class ActionChoices(models.TextChoices): WHATSAPP = "whatsapp", _("WhatsApp") VISIT = "visit", _("Visit") ADD_CAR = "add_car", _("Add Car") + SALE_CAR = "sale_car", _("Sale Car") RESERVE_CAR = "reserve_car", _("Reserve Car") + TRANSFER_CAR = "transfer_car", _("Transfer Car") REMOVE_CAR = "remove_car", _("Remove Car") CREATE_QUOTATION = "create_quotation", _("Create Quotation") CANCEL_QUOTATION = "cancel_quotation", _("Cancel Quotation") diff --git a/inventory/signals.py b/inventory/signals.py index c3c93e83..119255e2 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -647,9 +647,11 @@ def create_customer_user(sender, instance, created, **kwargs): user = User.objects.create( username=instance.email, email=instance.email, - first_name=instance.additional_info["customer_info"].get("first_name", ""), - last_name=instance.additional_info["customer_info"].get("last_name", ""), ) + customer_info = instance.additional_info.get("customer_info",None) + user.first_name = customer_info.get("first_name", None) if customer_info else "" + user.last_name = customer_info.get("last_name", None) if customer_info else "" + user.set_unusable_password() user.save() instance.user = user diff --git a/inventory/urls.py b/inventory/urls.py index b36ba066..bf0e59d7 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -199,6 +199,7 @@ urlpatterns = [ path("cars/inventory/stats", views.inventory_stats_view, name="inventory_stats"), path("cars/inventory/list", views.CarListView.as_view(), name="car_list"), path("cars//", views.CarDetailView.as_view(), name="car_detail"), + path("cars//history/", views.car_history, name="car_history"), path("cars//update/", views.CarUpdateView.as_view(), name="car_update"), path("cars//delete/", views.CarDeleteView.as_view(), name="car_delete"), path( diff --git a/inventory/utils.py b/inventory/utils.py index cf342940..7ca94675 100644 --- a/inventory/utils.py +++ b/inventory/utils.py @@ -456,7 +456,7 @@ class CarTransfer: self._add_car_item_to_invoice() - def _add_car_item_to_invoice(self): + def _add_car_item_to_invoice(self): self.item = self.from_dealer.entity.get_items_products().filter(name=self.car.vin).first() if not self.item: return @@ -474,9 +474,10 @@ class CarTransfer: commit=True, operation=InvoiceModel.ITEMIZE_APPEND, ) - self.invoice.save() - self.invoice.mark_as_review() - self.invoice.mark_as_approved(self.from_dealer.entity.slug, self.from_dealer.entity.admin) + + if self.invoice.can_review(): + self.invoice.mark_as_review() + self.invoice.mark_as_approved(self.from_dealer.entity.slug, self.from_dealer.entity.admin) self.invoice.save() def _create_product_in_receiver_ledger(self): @@ -488,6 +489,7 @@ class CarTransfer: coa_model=self.to_dealer.entity.get_default_coa(), ) + self.product.additional_info = {} self.product.additional_info.update({"car_info": self.car.to_dict()}) self.product.save() diff --git a/inventory/views.py b/inventory/views.py index 683ba0f4..f7086697 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -405,6 +405,10 @@ class CarCreateView(LoginRequiredMixin, CreateView): messages.success(self.request, "Car saved successfully.") return super().form_valid(form) +def car_history(request,pk): + car = get_object_or_404(models.Car, pk=pk) + activities = models.Activity.objects.filter(content_type__model="car", object_id=car.id) + return render(request,'inventory/car_history.html',{"car":car,"activities":activities}) class AjaxHandlerView(LoginRequiredMixin, View): def get(self, request, *args, **kwargs): @@ -1035,6 +1039,8 @@ def car_transfer_accept_reject(request, car_pk, transfer_pk): # success = CarTransfer(car, transfer) if success: messages.success(request, _("Car Transfer Completed successfully.")) + models.Activity.objects.create(content_object=car,notes=f"Transfered from {transfer.from_dealer} to {transfer.to_dealer}",created_by=request.user) + models.Notification.objects.create( user=transfer.from_dealer.user, message=f"Car transfer request from {transfer.to_dealer} is completed.", @@ -2693,7 +2699,7 @@ def create_sale_order(request, pk): except KeyError: pass - models.Car.objects.get(vin=item.item_model.name).mark_as_sold() + models.Car.objects.get(vin=item.item_model.name).mark_as_sold(user=request.user) messages.success(request, "Sale Order created successfully") return redirect("estimate_detail", pk=pk) diff --git a/templates/inventory/car_detail.html b/templates/inventory/car_detail.html index 101fabb6..2c0fea4d 100644 --- a/templates/inventory/car_detail.html +++ b/templates/inventory/car_detail.html @@ -6,21 +6,21 @@ {% if not car.ready %} -{% endif %} -{% if car.get_transfer %} - + {% endif %} + {% if car.get_transfer %} + -{% endif %} -{% if car.is_reserved %} - + {% endif %} + {% if car.is_reserved %} + diff --git a/templates/inventory/car_history.html b/templates/inventory/car_history.html new file mode 100644 index 00000000..657befd8 --- /dev/null +++ b/templates/inventory/car_history.html @@ -0,0 +1,47 @@ +{% extends 'base.html' %} +{% load i18n static custom_filters %} +{% block title %} + {{ _('Car Details') }} +{% endblock %} + +{% block content %} +
+
+
+
+
+
+
+
+
+

{{_('History')}}

+
+ {% for activity in activities %} +
+
+
+
+
+
+
+
+
+
+
{{activity.content_object}}
+
+

{{activity.created}}

+
+
by {{activity.created_by}}
+

{{activity.notes}}.

+
+
+
+ {% endfor %} +
+
+
+
+
+
+
+{% endblock %} diff --git a/templates/sales/estimates/estimate_form.html b/templates/sales/estimates/estimate_form.html index e50323af..325a6784 100644 --- a/templates/sales/estimates/estimate_form.html +++ b/templates/sales/estimates/estimate_form.html @@ -19,6 +19,8 @@