Compare commits

..

2 Commits

Author SHA1 Message Date
jordi fita mas fffab62135 Use left join between products and taxes
A product can have no tax.
2023-02-25 03:20:34 +01:00
jordi fita mas 419ac3ed46 Adjust invoice.css to work with WeasyPrint too
I am planning to use WeasyPrint to “generate PDF” from the same HTML
that the user view, but it seems that it does not support flex’s gap
and some other properties that i had to change to work in both user
agents.

I also moved the invoice’s “footer” inside the last product’s body
because i do not want the footer to be a “widow”.
2023-02-25 03:16:20 +01:00
4 changed files with 52 additions and 41 deletions

View File

@ -40,7 +40,7 @@ func GetProductForm(w http.ResponseWriter, r *http.Request, params httprouter.Pa
mustRenderNewProductForm(w, r, form) mustRenderNewProductForm(w, r, form)
return return
} }
if notFoundErrorOrPanic(conn.QueryRow(r.Context(), "select product.name, product.description, to_price(price, decimal_digits), array_agg(tax_id) from product join product_tax using (product_id) join company using (company_id) join currency using (currency_code) where product.slug = $1 group by product_id, product.name, product.description, price, decimal_digits", slug).Scan(form.Name, form.Description, form.Price, form.Tax)) { if notFoundErrorOrPanic(conn.QueryRow(r.Context(), "select product.name, product.description, to_price(price, decimal_digits), array_agg(tax_id) from product left join product_tax using (product_id) join company using (company_id) join currency using (currency_code) where product.slug = $1 group by product_id, product.name, product.description, price, decimal_digits", slug).Scan(form.Name, form.Description, form.Price, form.Tax)) {
http.NotFound(w, r) http.NotFound(w, r)
return return
} }

View File

@ -54,6 +54,9 @@ func mustRenderTemplate(wr io.Writer, r *http.Request, layout string, filename s
field.Attributes = append(field.Attributes, template.HTMLAttr(attr)) field.Attributes = append(field.Attributes, template.HTMLAttr(attr))
return field return field
}, },
"sub": func(y, x int) int {
return x - y
},
"deleteMethod": func() template.HTML { "deleteMethod": func() template.HTML {
return overrideMethodField(http.MethodDelete) return overrideMethodField(http.MethodDelete)
}, },

View File

@ -1,11 +1,10 @@
.invoice { .invoice {
display: flex; display: flex;
gap: 2.5rem;
} }
.invoice header { .invoice header {
flex: 1; flex: 1;
min-width: 20rem; min-width: 18rem;
padding: 0 1rem 0 0; padding: 0 1rem 0 0;
display: block; display: block;
background: initial; background: initial;
@ -24,8 +23,16 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 3; flex: 3;
gap: 5rem; padding: 0 0 0 2.5rem;
align-items: end; align-items: flex-end;
}
.invoice .invoicee, .invoice .invoicer {
min-width: 18rem;
}
.invoice table {
margin: 5rem 0;
} }
.invoice th { .invoice th {
@ -42,7 +49,7 @@
text-align: right; text-align: right;
} }
.invoice .notes, .invoice footer { .invoice .notes {
white-space: pre; white-space: pre;
text-align: right; text-align: right;
} }
@ -51,7 +58,7 @@
vertical-align: top; vertical-align: top;
} }
.invoice tbody, .invoice footer { .invoice tbody, .invoice .legal {
page-break-inside: avoid; page-break-inside: avoid;
} }
@ -59,7 +66,6 @@
background-color: initial; background-color: initial;
} }
.invoice tbody:not(:first-of-type) tr:first-child td, .invoice .tfoot th, .invoice .tfoot td { .invoice tbody:not(:first-of-type) tr:first-child td, .invoice .tfoot th, .invoice .tfoot td {
padding-top: 1em; padding-top: 1em;
} }
@ -72,8 +78,8 @@
max-width: 20rem; max-width: 20rem;
} }
.invoice footer { .invoice .legal {
margin-top: 8rem; margin-top: 16rem;
font-size: 1.2rem; font-size: 1.2rem;
} }

View File

@ -25,6 +25,15 @@
{{ .Invoicer.Email }}<br> {{ .Invoicer.Email }}<br>
{{ .Invoicer.Phone }}<br> {{ .Invoicer.Phone }}<br>
</address> </address>
<p class="legal">Oriol Carbonell Pujolàs és Responsable del Tractament de les seves dades d'acord
amb el RGPD i la LOPDGDDGDD, i les tracta per a mantenir una relació
mercantil/comercial amb vostè. Les conservarà mentre es mantingui aquesta relació
i no es comunicaran a tercers. Pot exercir els drets d'accés, rectificació, portabilitat,
supressió, limitació i oposició a Oriol Carbonell pUJOLÀS, amb domicili Carrer dels
Sastres 14, 17800 Olot o enviant un correu electrònic a info@visavis.cat. Per a
qualsevol reclamació pot acudir a agpd.es. Per a més informació pot consultar la
nostra política de privacitat a www.visavis.cat.</p>
</header> </header>
<div> <div>
@ -44,9 +53,10 @@
<th class="numeric">{{( pgettext "Subtotal" "title" )}}</th> <th class="numeric">{{( pgettext "Subtotal" "title" )}}</th>
</tr> </tr>
</thead> </thead>
{{ range $product := .Products -}} {{ $lastIndex := len .Products | sub 1 }}
{{ range $index, $product := .Products -}}
<tbody>
{{ if .Description }} {{ if .Description }}
<tbody>
<tr class="name"> <tr class="name">
<td colspan="4">{{ .Name }}</td> <td colspan="4">{{ .Name }}</td>
</tr> </tr>
@ -56,47 +66,39 @@
<td class="numeric">{{ .Quantity }}</td> <td class="numeric">{{ .Quantity }}</td>
<td class="numeric">{{ .Total | formatPrice }}</td> <td class="numeric">{{ .Total | formatPrice }}</td>
</tr> </tr>
</tbody>
{{ else }} {{ else }}
<tbody>
<tr class="name"> <tr class="name">
<td>{{ .Name }}</td> <td>{{ .Name }}</td>
<td class="numeric">{{ .Price | formatPrice }}</td> <td class="numeric">{{ .Price | formatPrice }}</td>
<td class="numeric">{{ .Quantity }}</td> <td class="numeric">{{ .Quantity }}</td>
<td class="numeric">{{ .Total | formatPrice }}</td> <td class="numeric">{{ .Total | formatPrice }}</td>
</tr> </tr>
</tbody>
{{- end }} {{- end }}
{{ if (eq $index $lastIndex) }}
<tr class="tfoot">
<th scope="row" colspan="3">{{( pgettext "Subtotal" "title" )}}</th>
<td class="numeric">{{ $.Subtotal | formatPrice }}</td>
</tr>
{{ range $tax := $.Taxes -}}
<tr class="tfoot">
<th scope="row" colspan="3">{{ index . 0 }}</th>
<td class="numeric">{{ index . 1 | formatPrice }}</td>
</tr>
{{- end }}
<tr class="tfoot">
<th scope="row" colspan="3">{{( pgettext "Total" "title" )}}</th>
<td class="numeric">{{ $.Total | formatPrice }}</td>
</tr>
{{ end }}
</tbody>
{{- end }} {{- end }}
<tbody class="tfoot">
<tr>
<th scope="row" colspan="3">{{( pgettext "Subtotal" "title" )}}</th>
<td class="numeric">{{ .Subtotal | formatPrice }}</td>
</tr>
{{ range $tax := .Taxes -}}
<tr>
<th scope="row" colspan="3">{{ index . 0 }}</th>
<td class="numeric">{{ index . 1 | formatPrice }}</td>
</tr>
{{- end }}
<tr>
<th scope="row" colspan="3">{{( pgettext "Total" "title" )}}</th>
<td class="numeric">{{ .Total | formatPrice }}</td>
</tr>
</tbody>
</table> </table>
<div class="notes">{{ .Notes }}</div> {{ if .Notes -}}
<p class="notes">{{ .Notes }}</p>
{{- end }}
<footer>Oriol Carbonell Pujolàs és Responsable del Tractament de les seves dades d'acord
amb el RGPD i la LOPDGDDGDD, i les tracta per a mantenir una relació
mercantil/comercial amb vostè. Les conservarà mentre es mantingui aquesta relació
i no es comunicaran a tercers. Pot exercir els drets d'accés, rectificació, portabilitat,
supressió, limitació i oposició a Oriol Carbonell pUJOLÀS, amb domicili Carrer dels
Sastres 14, 17800 Olot o enviant un correu electrònic a info@visavis.cat. Per a
qualsevol reclamació pot acudir a agpd.es. Per a més informació pot consultar la
nostra política de privacitat a www.visavis.cat.
</footer>
</div> </div>
</article> </article>
{{- end}} {{- end}}