need help for rendering data in frontend

good day, need help in my django template, I’ve been struggling to figure out how can I put the subtotal below each Item Category.

here is my template:

{% load crispy_forms_tags %}
{% load static %}
{% load romanize %}
{% load humanize %}
{% csrf_token %}

<table class="table table-bordered" id="boqs">

    <thead>
    <tr>
        <th class="text-center py-1">Item</th>
        <th class="text-center py-1">Description</th>
        <th class="text-center py-1">Qty</th>
        <th class="text-center py-1">Unit</th>
        <th class="text-center py-1">Unit Cost</th>
        <th class="text-center py-1">Material Cost</th>
        <th class="text-center py-1">Unit Cost</th>
        <th class="text-center py-1">Labor Cost</th>
        <th class="text-center py-1">Line Total</th>
        <th class="text-center py-1">Actions</th>
    </tr>
    </thead>
    <tbody>
    {% for boq in boqs %}
        {% ifchanged boq.item %}
            <tr class="bg-light">
                <td style="text-align: center; font-weight: bold" type="I">{{ boq.item|romanize }}.</td>
                <td style="text-align: left; font-weight: bold" colspan="9">{{ boq.get_item_display }}</td>
            </tr>
        {% endifchanged %}
        <tr>
            <td class="text-start"></td>
            <td class="text-start">{{ boq.description }}</td>
            <td class="text-center">{{ boq.quantity }}</td>
            <td class="text-center">{{ boq.unit }}</td>
            <td class="text-center">{{ boq.rate_material|intcomma }}</td>
            <td class="text-center">{{ boq.material_costs|intcomma }}</td>
            <td class="text-center">{{ boq.rate_labor|intcomma }}</td>
            <td class="text-center">{{ boq.labor_costs|intcomma }}</td>
            <td class="text-center">{{ boq.costs|intcomma }}</td>
            <td class="px-2 py-1 text-center">
                <button type="button" class="boq-edit btn btn-warning" data-form-url="{% url 'boq-update' boq.pk %}">
                    <span class="fa fa-pen"></span>
                </button>
                <a href="{% url 'boq-delete' boq.pk %}" class="btn btn-danger">
                    <span class="fa fa-trash"></span>
                </a>
            </td>
        </tr>
        {% if forloop.counter0 %}
            <tr class="bg-light">
                <td colspan="2" class="text-bold text-end">SubTotal</td>
                <td></td>
                <td></td>
                <td></td>
                <td class="text-center">{{ subtotal_material_cost|intcomma }}</td>
                <td></td>
                <td class="text-center">subTotal Labor Cost</td>
                <td class="text-center">SubTotal Line Total</td>
                <td></td>
            </tr>
        {% endif %}
    {% endfor %}
    </tbody>
</table>

this is the output:

this is the output that I’m trying to figure

this is my views.py

@method_decorator(admin_required, name='dispatch')
class BOQList(LoginRequiredMixin, ListView):
    model = BOQ
    template_name = 'pms/boq/boq-list-copy.html'
    context_object_name = 'boqs'

    def get_queryset(self):  # new
        project_id = self.kwargs['project_id']
        return BOQ.objects.filter(project_id=project_id).order_by_item()

this is my models.py

class ItemQuerySet(models.QuerySet):  # WORKING
	def order_by_item(self):
		return self.alias(item_order=Case(
			When(item=BOQ.ItemDesciption.GENERAL_REQUIREMENTS, then=Value(1)),
			When(item=BOQ.ItemDesciption.SITE_WORK, then=Value(2)),
			When(item=BOQ.ItemDesciption.FORMS_SCAFFOLDS, then=Value(3)),
			When(item=BOQ.ItemDesciption.STRUCTURAL_CONCRETE, then=Value(4)),
			When(item=BOQ.ItemDesciption.STRUCTURAL_STEEL, then=Value(5)),
			When(item=BOQ.ItemDesciption.ROOFING, then=Value(6)),
			When(item=BOQ.ItemDesciption.WATERPROOFING, then=Value(7)),
			When(item=BOQ.ItemDesciption.MASONRY, then=Value(8)),
			When(item=BOQ.ItemDesciption.WALL_PLASTERING, then=Value(9)),
			When(item=BOQ.ItemDesciption.PAINTING_WORKS, then=Value(10)),
			When(item=BOQ.ItemDesciption.WALL_FINISHES, then=Value(11)),
			When(item=BOQ.ItemDesciption.FLOOR_FINISHES, then=Value(12)),
			When(item=BOQ.ItemDesciption.CEILING_WORKS, then=Value(13)),
			When(item=BOQ.ItemDesciption.DOORS, then=Value(14)),
			When(item=BOQ.ItemDesciption.WINDOWS, then=Value(15)),
			When(item=BOQ.ItemDesciption.ELECTRICAL_WORKS, then=Value(16)),
			When(item=BOQ.ItemDesciption.PLUMBING_WORKS, then=Value(17)),
			When(item=BOQ.ItemDesciption.MILLWORKS, then=Value(18)),
		)).order_by("item_order", "description")

class BOQ(models.Model):  # WORKING
	
	class ItemDesciption(models.IntegerChoices):
		GENERAL_REQUIREMENTS = 1, _("GENERAL REQUIREMENTS")
		SITE_WORK = 2, _("SITE WORK")
		FORMS_SCAFFOLDS = 3, _("FORMS & SCAFFOLDS")
		STRUCTURAL_CONCRETE = 4, _("STRUCTURAL CONCRETE")
		STRUCTURAL_STEEL = 5, _("STRUCTURAL STEEL")
		ROOFING = 6, _("ROOFING")
		WATERPROOFING = 7, _("WATERPROOFING")
		MASONRY = 8, _("MASONRY")
		WALL_PLASTERING = 9, _("WALL PLASTERING")
		PAINTING_WORKS = 10, _("PAINTING WORKS")
		WALL_FINISHES = 11, _("WALL FINISHES")
		FLOOR_FINISHES = 12, _("FLOOR FINISHES")
		CEILING_WORKS = 13, _("CEILING WORKS")
		DOORS = 14, _("DOORS")
		WINDOWS = 15, _("WINDOWS")
		ELECTRICAL_WORKS = 16, _("ELECTRICAL WORKS")
		PLUMBING_WORKS = 17, _("PLUMBING WORKS")
		MILLWORKS = 18, _("MILLWORKS")

	class Units(models.TextChoices):
		LOT = "lot", "lot"
		CUM = "cum", "cum"
		SQM = "sqm", "sqm"
		BFT = "bft", "bft"
		KGS = "kgs", "kgs"
		LM = "lm", "lm"
		PCS = "pcs", "pcs"
		SETS = "sets", "sets"
		PAIRS = "pairs", "pairs"
		METERS = "meters", "meters"
		CBM = "cbm", "cbm"
	
	project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True, blank=True)
	item = models.PositiveSmallIntegerField(choices=ItemDesciption.choices, default=ItemDesciption.GENERAL_REQUIREMENTS, db_index=True)
	description = models.CharField(max_length=250, null=True, blank=True)
	quantity = models.DecimalField(max_digits=10, decimal_places=2, default=1)
	unit = models.CharField(max_length=10, choices=Units.choices, default=Units.LOT)
	rate_material = models.DecimalField(_("Unit Cost for Material"), max_digits=10, decimal_places=2, default=0)
	material_cost = models.DecimalField(_("Material Cost"), max_digits=10, decimal_places=2, default=0)
	rate_labor = models.DecimalField(_("Unit Cost for Labor"), max_digits=10, decimal_places=2, default=0)
	labor_cost = models.DecimalField(_("Labor Cost"), max_digits=10, decimal_places=2, default=0)
	line_total = models.DecimalField(_("Line Total"), max_digits=10, decimal_places=2, default=0)
	
	# SUBTOTAL
	subTotal_material_cost = models.DecimalField(_("Subtotal Material Cost"), max_digits=10, decimal_places=2, default=0)
	subTotal_labor_cost = models.DecimalField(_("Subtotal Material Cost"), max_digits=10, decimal_places=2, default=0)
	subTotal_line_total = models.DecimalField(_("Subtotal Material Cost"), max_digits=10, decimal_places=2, default=0)

	# GRANDTOTAL
	grandTotal_material_cost = models.DecimalField(_("Grand Total Material Cost"), max_digits=10, decimal_places=2, default=0)
	grandTotal_labor_cost = models.DecimalField(_("Grand Total Material Cost"), max_digits=10, decimal_places=2, default=0)
	grandTotal_line_total = models.DecimalField(_("Grand Total Material Cost"), max_digits=10, decimal_places=2, default=0)

	created = models.DateTimeField(auto_now_add=True)
	updated = models.DateTimeField(auto_now=True)
	
	objects = ItemQuerySet.as_manager()
	
	class Meta:
		verbose_name = "Bill Of Quantities"
		verbose_name_plural = "Bill Of Quantities"
		ordering = ['item', 'description']
	
	def __str__(self):
		return self.description
	
	@property
	def material_costs(self):
		self.material_cost = self.rate_material * self.quantity
		return self.material_cost.quantize(Decimal('0.00'))
	
	@property
	def labor_costs(self):
		self.labor_cost = self.rate_labor * self.quantity
		return self.labor_cost.quantize(Decimal('0.00'))
	
	@property
	def costs(self):
		self.line_total = self.material_costs + self.labor_costs
		return self.line_total.quantize(Decimal('0.00'))
	
	@property
	def get_subtotal_material_cost(self):
		subtotal = BOQ.objects.filter(item=self.item).aggregate(Sum('material_cost'))
		self.subTotal_material_cost = subtotal
		return self.subTotal_material_cost.quantize(Decimal('0.00'))

To make sure I understand the situation - the issue is that your current template is showing the SubTotal line multiple times for each category (e.g. GENERAL REQUIREMENTS) when you only want it to appear once at the bottom for that category. Is that correct?

I’m also confused by your model.

It appears that GENERAL REQUIREMENTS is an item, but what are the entries for Building Permit and City Tax, etc? If they are descriptions, then my first recommendation would be to normalize your database structures. You should split these out into two separate models. There would be a model for the Item, and a separate model for the Description with a ForeignKey to Item.

the issue is that your current template is showing the SubTotal line multiple times for each category (e.g. GENERAL REQUIREMENTS ) when you only want it to appear once at the bottom for that category. Is that correct?

Yes sir, that’s the output that I want to appear in the HTML

It appears that GENERAL REQUIREMENTS is an item , but what are the entries for Building Permit and City Tax , etc?

The Building permit and city tax and others are description for the item like GENERAL REQUIREMENTS

then my first recommendation would be to normalize your database structures. You should split these out into two separate models. There would be a model for the Item , and a separate model for the Description with a ForeignKey to Item .

correct me if I’m wrong sir, the parent model is the Item and the child model will be the Description, am I right?

That would be correct. Additionally, the child model would also contain the numeric fields associated with those descriptions.

1 Like