Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 667617296b | |||
| 1c61111da3 |
@@ -462,34 +462,34 @@ export function InvoiceDetailView({ detail, creditNotes = [] }: Props) {
|
|||||||
<table className="w-full text-sm">
|
<table className="w-full text-sm">
|
||||||
<thead className="text-xs text-text-muted text-left">
|
<thead className="text-xs text-text-muted text-left">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="pb-2">{t("creditNoteNumberHeader")}</th>
|
<th className="pb-2 pr-4">{t("creditNoteNumberHeader")}</th>
|
||||||
<th className="pb-2">{t("creditNoteKindHeader")}</th>
|
<th className="pb-2 pr-4">{t("creditNoteKindHeader")}</th>
|
||||||
<th className="pb-2 text-right">
|
<th className="pb-2 pr-4 text-right">
|
||||||
{t("creditNoteAmountHeader")}
|
{t("creditNoteAmountHeader")}
|
||||||
</th>
|
</th>
|
||||||
<th className="pb-2">{t("creditNoteReasonHeader")}</th>
|
<th className="pb-2 pr-4">{t("creditNoteReasonHeader")}</th>
|
||||||
<th className="pb-2">{t("creditNoteIssuedHeader")}</th>
|
<th className="pb-2 pr-4">{t("creditNoteIssuedHeader")}</th>
|
||||||
<th className="pb-2 text-right">{t("creditNotePdfHeader")}</th>
|
<th className="pb-2 text-right">{t("creditNotePdfHeader")}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{creditNotes.map((cn) => (
|
{creditNotes.map((cn) => (
|
||||||
<tr key={cn.id} className="border-t border-border">
|
<tr key={cn.id} className="border-t border-border">
|
||||||
<td className="py-2 font-mono text-xs">
|
<td className="py-2 pr-4 font-mono text-xs">
|
||||||
{cn.creditNoteNumber}
|
{cn.creditNoteNumber}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2">
|
<td className="py-2 pr-4">
|
||||||
<span className="px-2 py-0.5 rounded text-xs text-error bg-error/10">
|
<span className="px-2 py-0.5 rounded text-xs text-error bg-error/10">
|
||||||
{t(`creditNoteKind_${cn.kind}` as any)}
|
{t(`creditNoteKind_${cn.kind}` as any)}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 text-right font-mono">
|
<td className="py-2 pr-4 text-right font-mono whitespace-nowrap">
|
||||||
CHF {cn.amountChf.toFixed(2)}
|
CHF {cn.amountChf.toFixed(2)}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 text-text-secondary text-xs">
|
<td className="py-2 pr-4 text-text-secondary text-xs">
|
||||||
{cn.reason ?? "—"}
|
{cn.reason ?? "—"}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 text-xs text-text-muted">
|
<td className="py-2 pr-4 text-xs text-text-muted whitespace-nowrap">
|
||||||
{cn.issuedAt.slice(0, 10)}
|
{cn.issuedAt.slice(0, 10)}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 text-right">
|
<td className="py-2 text-right">
|
||||||
|
|||||||
@@ -39,11 +39,11 @@ export function CustomerCreditNoteList({ creditNotes }: Props) {
|
|||||||
<table className="w-full text-sm">
|
<table className="w-full text-sm">
|
||||||
<thead className="text-xs text-text-muted text-left">
|
<thead className="text-xs text-text-muted text-left">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="pb-2">{t("creditNoteNumberCol")}</th>
|
<th className="pb-2 pr-4">{t("creditNoteNumberCol")}</th>
|
||||||
<th className="pb-2">{t("creditNoteInvoiceCol")}</th>
|
<th className="pb-2 pr-4">{t("creditNoteInvoiceCol")}</th>
|
||||||
<th className="pb-2">{t("creditNoteIssuedCol")}</th>
|
<th className="pb-2 pr-4">{t("creditNoteIssuedCol")}</th>
|
||||||
<th className="pb-2 text-right">{t("creditNoteAmountCol")}</th>
|
<th className="pb-2 pr-4 text-right">{t("creditNoteAmountCol")}</th>
|
||||||
<th className="pb-2 text-right">{t("creditNoteKindCol")}</th>
|
<th className="pb-2 pr-4 text-right">{t("creditNoteKindCol")}</th>
|
||||||
<th className="pb-2 text-right">{t("creditNotePdfCol")}</th>
|
<th className="pb-2 text-right">{t("creditNotePdfCol")}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -53,19 +53,19 @@ export function CustomerCreditNoteList({ creditNotes }: Props) {
|
|||||||
key={cn.id}
|
key={cn.id}
|
||||||
className="border-t border-border align-middle"
|
className="border-t border-border align-middle"
|
||||||
>
|
>
|
||||||
<td className="py-2 font-mono text-xs">
|
<td className="py-2 pr-4 font-mono text-xs">
|
||||||
{cn.creditNoteNumber}
|
{cn.creditNoteNumber}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 font-mono text-xs text-text-secondary">
|
<td className="py-2 pr-4 font-mono text-xs text-text-secondary">
|
||||||
{cn.invoiceNumber}
|
{cn.invoiceNumber}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 text-text-secondary">
|
<td className="py-2 pr-4 text-text-secondary whitespace-nowrap">
|
||||||
{fmt.dateTime(new Date(cn.issuedAt), { dateStyle: "medium" })}
|
{fmt.dateTime(new Date(cn.issuedAt), { dateStyle: "medium" })}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 text-right font-mono">
|
<td className="py-2 pr-4 text-right font-mono whitespace-nowrap">
|
||||||
CHF {cn.amountChf.toFixed(2)}
|
CHF {cn.amountChf.toFixed(2)}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 text-right">
|
<td className="py-2 pr-4 text-right">
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-0.5 rounded text-xs ${
|
className={`px-2 py-0.5 rounded text-xs ${
|
||||||
kindColors[cn.kind] ?? ""
|
kindColors[cn.kind] ?? ""
|
||||||
|
|||||||
@@ -24,9 +24,8 @@ import {
|
|||||||
renderToBuffer,
|
renderToBuffer,
|
||||||
} from "@react-pdf/renderer";
|
} from "@react-pdf/renderer";
|
||||||
import type { CreditNote, Invoice } from "@/types";
|
import type { CreditNote, Invoice } from "@/types";
|
||||||
import { BRAND, Logo, ACCENT_CREDIT_NOTE } from "./pdf-brand";
|
import { BRAND, Logo } from "./pdf-brand";
|
||||||
|
|
||||||
const ACCENT = ACCENT_CREDIT_NOTE;
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Localized strings
|
// Localized strings
|
||||||
@@ -207,15 +206,15 @@ const styles = StyleSheet.create({
|
|||||||
logoBlock: { flexDirection: "row", alignItems: "center" },
|
logoBlock: { flexDirection: "row", alignItems: "center" },
|
||||||
brandName: {
|
brandName: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: ACCENT.primaryDark,
|
color: BRAND.primaryDark,
|
||||||
marginLeft: 8,
|
marginLeft: 8,
|
||||||
fontFamily: "Helvetica-Bold",
|
fontFamily: "Helvetica-Bold",
|
||||||
},
|
},
|
||||||
issuerBlock: { textAlign: "right", fontSize: 8.5, color: BRAND.mutedColor },
|
issuerBlock: { textAlign: "right", fontSize: 8.5, color: BRAND.mutedColor },
|
||||||
issuerName: { fontSize: 11, color: ACCENT.primaryDark, marginBottom: 2 },
|
issuerName: { fontSize: 11, color: BRAND.primaryDark, marginBottom: 2 },
|
||||||
docTitle: {
|
docTitle: {
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
color: ACCENT.primaryDark,
|
color: BRAND.primaryDark,
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
fontFamily: "Helvetica-Bold",
|
fontFamily: "Helvetica-Bold",
|
||||||
},
|
},
|
||||||
@@ -230,9 +229,9 @@ const styles = StyleSheet.create({
|
|||||||
billTo: {
|
billTo: {
|
||||||
marginBottom: 24,
|
marginBottom: 24,
|
||||||
padding: 8,
|
padding: 8,
|
||||||
backgroundColor: "#fdf2f2",
|
backgroundColor: "#f7f7f5",
|
||||||
borderLeftWidth: 3,
|
borderLeftWidth: 3,
|
||||||
borderLeftColor: ACCENT.primary,
|
borderLeftColor: BRAND.primary,
|
||||||
},
|
},
|
||||||
billToLabel: { fontSize: 8, color: BRAND.mutedColor, marginBottom: 4 },
|
billToLabel: { fontSize: 8, color: BRAND.mutedColor, marginBottom: 4 },
|
||||||
billToName: { fontSize: 11, marginBottom: 2 },
|
billToName: { fontSize: 11, marginBottom: 2 },
|
||||||
@@ -245,7 +244,7 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
amountHeader: {
|
amountHeader: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
backgroundColor: ACCENT.primaryDark,
|
backgroundColor: BRAND.primaryDark,
|
||||||
color: "#ffffff",
|
color: "#ffffff",
|
||||||
paddingVertical: 5,
|
paddingVertical: 5,
|
||||||
paddingHorizontal: 6,
|
paddingHorizontal: 6,
|
||||||
@@ -273,17 +272,17 @@ const styles = StyleSheet.create({
|
|||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
borderTopWidth: 1,
|
borderTopWidth: 1,
|
||||||
borderTopColor: ACCENT.primaryDark,
|
borderTopColor: BRAND.primaryDark,
|
||||||
paddingTop: 6,
|
paddingTop: 6,
|
||||||
marginTop: 4,
|
marginTop: 4,
|
||||||
},
|
},
|
||||||
totalsGrandLabel: {
|
totalsGrandLabel: {
|
||||||
color: ACCENT.primaryDark,
|
color: BRAND.primaryDark,
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
fontFamily: "Helvetica-Bold",
|
fontFamily: "Helvetica-Bold",
|
||||||
},
|
},
|
||||||
totalsGrandValue: {
|
totalsGrandValue: {
|
||||||
color: ACCENT.primaryDark,
|
color: BRAND.primaryDark,
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
fontFamily: "Helvetica-Bold",
|
fontFamily: "Helvetica-Bold",
|
||||||
@@ -353,7 +352,7 @@ function CreditNotePdfDocument({ creditNote, invoice }: CreditNotePdfProps) {
|
|||||||
Issuer block from BRAND.issuer (shared with invoice). */}
|
Issuer block from BRAND.issuer (shared with invoice). */}
|
||||||
<View style={styles.headerRow}>
|
<View style={styles.headerRow}>
|
||||||
<View style={styles.logoBlock}>
|
<View style={styles.logoBlock}>
|
||||||
<Logo size={42} color={ACCENT.primary} />
|
<Logo size={42} color={BRAND.primary} />
|
||||||
<Text style={styles.brandName}>{BRAND.name}</Text>
|
<Text style={styles.brandName}>{BRAND.name}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.issuerBlock}>
|
<View style={styles.issuerBlock}>
|
||||||
|
|||||||
@@ -1261,10 +1261,12 @@ export async function sendCreditNoteEmail(params: {
|
|||||||
const safeNumberINV = escapeHtml(params.invoiceNumber);
|
const safeNumberINV = escapeHtml(params.invoiceNumber);
|
||||||
const safeReason = params.reason ? escapeHtml(params.reason) : null;
|
const safeReason = params.reason ? escapeHtml(params.reason) : null;
|
||||||
|
|
||||||
// Red accent (#DC2626) for the credit-note emails, mirroring the
|
// PieCed brand emerald — same accent the invoice email uses.
|
||||||
// PDF accent so the document family reads visually consistent.
|
// A credit note is still a PieCed IT document; the company
|
||||||
// The invoice email uses emerald; the credit note uses red.
|
// identity stays consistent across the document family. The
|
||||||
const ACCENT = "#DC2626";
|
// doc type is distinguished by the subject line and copy, not
|
||||||
|
// by colour.
|
||||||
|
const ACCENT = "#10B981";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await getTransporter().sendMail({
|
await getTransporter().sendMail({
|
||||||
|
|||||||
@@ -57,24 +57,16 @@ export const BRAND = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Accent colours for document variants — credit notes are red so
|
// Logo — PieCed's hexagon-pattern mark. Same shape used everywhere
|
||||||
// customers can tell them apart from invoices at a glance.
|
// and same brand colour. The credit note is still a PieCed IT
|
||||||
// ---------------------------------------------------------------------------
|
// document and reads with the same company identity as an invoice.
|
||||||
|
|
||||||
export const ACCENT_CREDIT_NOTE = {
|
|
||||||
primary: "#DC2626",
|
|
||||||
primaryDark: "#991B1B",
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Logo — PieCed's hexagon-pattern mark. Same shape used everywhere;
|
|
||||||
// only the colour changes per document type.
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
interface LogoProps {
|
interface LogoProps {
|
||||||
size?: number;
|
size?: number;
|
||||||
/** Defaults to BRAND.primary (emerald). Pass ACCENT_CREDIT_NOTE.primary
|
/** Defaults to BRAND.primary. Override only for special cases
|
||||||
* on credit notes for the red variant. */
|
* (e.g. an inverse variant on a dark background). Standard
|
||||||
|
* documents — invoices, credit notes — all use BRAND.primary. */
|
||||||
color?: string;
|
color?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user