feat(companies): add invoiceEmail field separate from contact email

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hardik 2026-05-31 01:32:17 +05:30
parent e308d86e93
commit 4cb927cbd0
7 changed files with 25 additions and 9 deletions

View file

@ -15,6 +15,7 @@ const companySchema = z.object({
telephone: z.string().optional(), telephone: z.string().optional(),
mobile: z.string().optional(), mobile: z.string().optional(),
email: z.string().email("Invalid email").optional().or(z.literal("")), email: z.string().email("Invalid email").optional().or(z.literal("")),
invoiceEmail: z.string().email("Invalid invoice email").optional().or(z.literal("")),
invoiceAddress: z.string().optional(), invoiceAddress: z.string().optional(),
}); });
@ -31,13 +32,14 @@ export async function createCompany(formData: FormData): Promise<ActionResult> {
telephone: (formData.get("telephone") as string) || undefined, telephone: (formData.get("telephone") as string) || undefined,
mobile: (formData.get("mobile") as string) || undefined, mobile: (formData.get("mobile") as string) || undefined,
email: (formData.get("email") as string) || undefined, email: (formData.get("email") as string) || undefined,
invoiceEmail: (formData.get("invoiceEmail") as string) || undefined,
invoiceAddress: (formData.get("invoiceAddress") as string) || undefined, invoiceAddress: (formData.get("invoiceAddress") as string) || undefined,
}); });
if (!parsed.success) return { error: parsed.error.errors[0]?.message ?? "Validation failed" }; if (!parsed.success) return { error: parsed.error.errors[0]?.message ?? "Validation failed" };
const { name, gstNumber, address, telephone, mobile, email, invoiceAddress } = parsed.data; const { name, gstNumber, address, telephone, mobile, email, invoiceEmail, invoiceAddress } = parsed.data;
await db.company.create({ await db.company.create({
data: { name, gstNumber: gstNumber ?? null, address: address ?? null, telephone: telephone ?? null, mobile: mobile ?? null, email: email || null, invoiceAddress: invoiceAddress ?? null }, data: { name, gstNumber: gstNumber ?? null, address: address ?? null, telephone: telephone ?? null, mobile: mobile ?? null, email: email || null, invoiceEmail: invoiceEmail || null, invoiceAddress: invoiceAddress ?? null },
}); });
revalidatePath("/admin/companies"); revalidatePath("/admin/companies");
return { ok: true }; return { ok: true };
@ -59,14 +61,15 @@ export async function updateCompany(formData: FormData): Promise<ActionResult> {
telephone: (formData.get("telephone") as string) || undefined, telephone: (formData.get("telephone") as string) || undefined,
mobile: (formData.get("mobile") as string) || undefined, mobile: (formData.get("mobile") as string) || undefined,
email: (formData.get("email") as string) || undefined, email: (formData.get("email") as string) || undefined,
invoiceEmail: (formData.get("invoiceEmail") as string) || undefined,
invoiceAddress: (formData.get("invoiceAddress") as string) || undefined, invoiceAddress: (formData.get("invoiceAddress") as string) || undefined,
}); });
if (!parsed.success) return { error: parsed.error.errors[0]?.message ?? "Validation failed" }; if (!parsed.success) return { error: parsed.error.errors[0]?.message ?? "Validation failed" };
const { name, gstNumber, address, telephone, mobile, email, invoiceAddress } = parsed.data; const { name, gstNumber, address, telephone, mobile, email, invoiceEmail, invoiceAddress } = parsed.data;
await db.company.update({ await db.company.update({
where: { id }, where: { id },
data: { name, gstNumber: gstNumber ?? null, address: address ?? null, telephone: telephone ?? null, mobile: mobile ?? null, email: email || null, invoiceAddress: invoiceAddress ?? null }, data: { name, gstNumber: gstNumber ?? null, address: address ?? null, telephone: telephone ?? null, mobile: mobile ?? null, email: email || null, invoiceEmail: invoiceEmail || null, invoiceAddress: invoiceAddress ?? null },
}); });
revalidatePath("/admin/companies"); revalidatePath("/admin/companies");
return { ok: true }; return { ok: true };

View file

@ -15,6 +15,7 @@ export type CompanyRow = {
telephone: string | null; telephone: string | null;
mobile: string | null; mobile: string | null;
email: string | null; email: string | null;
invoiceEmail: string | null;
invoiceAddress: string | null; invoiceAddress: string | null;
isActive: boolean; isActive: boolean;
}; };
@ -91,7 +92,8 @@ export function CompaniesTable({ companies }: { companies: CompanyRow[] }) {
{c.telephone && <p> {c.telephone}</p>} {c.telephone && <p> {c.telephone}</p>}
{c.mobile && <p>📱 {c.mobile}</p>} {c.mobile && <p>📱 {c.mobile}</p>}
{c.email && <p> {c.email}</p>} {c.email && <p> {c.email}</p>}
{!c.telephone && !c.mobile && !c.email && <span className="italic text-neutral-400"></span>} {c.invoiceEmail && <p className="text-neutral-400">📄 {c.invoiceEmail}</p>}
{!c.telephone && !c.mobile && !c.email && !c.invoiceEmail && <span className="italic text-neutral-400"></span>}
</td> </td>
<td className="px-4 py-3"> <td className="px-4 py-3">
<span className={`rounded-full px-2.5 py-0.5 text-xs font-medium ${c.isActive ? "bg-success-100 text-success-700" : "bg-neutral-100 text-neutral-500"}`}> <span className={`rounded-full px-2.5 py-0.5 text-xs font-medium ${c.isActive ? "bg-success-100 text-success-700" : "bg-neutral-100 text-neutral-500"}`}>

View file

@ -13,6 +13,7 @@ type CompanyRow = {
telephone: string | null; telephone: string | null;
mobile: string | null; mobile: string | null;
email: string | null; email: string | null;
invoiceEmail: string | null;
invoiceAddress: string | null; invoiceAddress: string | null;
isActive: boolean; isActive: boolean;
}; };
@ -33,8 +34,12 @@ function CompanyFormFields({ company }: { company?: CompanyRow }) {
<input name="gstNumber" defaultValue={company?.gstNumber ?? ""} className={INPUT} placeholder="e.g. 27AAHCP5787B1Z6" /> <input name="gstNumber" defaultValue={company?.gstNumber ?? ""} className={INPUT} placeholder="e.g. 27AAHCP5787B1Z6" />
</div> </div>
<div> <div>
<label className={LABEL}>Email</label> <label className={LABEL}>Contact Email</label>
<input name="email" type="email" defaultValue={company?.email ?? ""} className={INPUT} placeholder="accounts@company.com" /> <input name="email" type="email" defaultValue={company?.email ?? ""} className={INPUT} placeholder="contact@company.com" />
</div>
<div>
<label className={LABEL}>Invoice Email <span className="font-normal text-neutral-400">(shown on POs)</span></label>
<input name="invoiceEmail" type="email" defaultValue={company?.invoiceEmail ?? ""} className={INPUT} placeholder="accounts@company.com" />
</div> </div>
<div> <div>
<label className={LABEL}>Telephone</label> <label className={LABEL}>Telephone</label>

View file

@ -26,6 +26,7 @@ export default async function AdminCompaniesPage() {
telephone: c.telephone, telephone: c.telephone,
mobile: c.mobile, mobile: c.mobile,
email: c.email, email: c.email,
invoiceEmail: c.invoiceEmail,
invoiceAddress: c.invoiceAddress, invoiceAddress: c.invoiceAddress,
isActive: c.isActive, isActive: c.isActive,
}))} }))}

View file

@ -74,9 +74,11 @@ export async function GET(request: NextRequest, { params }: Props) {
const CO_TEL = telParts.length > 0 ? telParts.join(" / ") : DEFAULT_CO_TEL; const CO_TEL = telParts.length > 0 ? telParts.join(" / ") : DEFAULT_CO_TEL;
const INV_ADDR = co?.invoiceAddress ?? (co?.address ? `${co.name}, ${co.address}` : DEFAULT_INV_ADDR); const INV_ADDR = co?.invoiceAddress ?? (co?.address ? `${co.name}, ${co.address}` : DEFAULT_INV_ADDR);
// invoiceEmail takes priority over general email for the Invoice Details line
const invoiceContactEmail = co?.invoiceEmail ?? co?.email ?? null;
const INV_GST = [ const INV_GST = [
co?.email ? `Email: ${co.email}` : null, invoiceContactEmail ? `Email: ${invoiceContactEmail}` : null,
co?.gstNumber ? `GST NO: ${co.gstNumber}` : null, co?.gstNumber ? `GST NO: ${co.gstNumber}` : null,
].filter(Boolean).join(" ") || DEFAULT_INV_GST; ].filter(Boolean).join(" ") || DEFAULT_INV_GST;
// ── Computed data ───────────────────────────────────────────────────────── // ── Computed data ─────────────────────────────────────────────────────────

View file

@ -0,0 +1,2 @@
-- Add invoiceEmail field to Company (separate from main contact email)
ALTER TABLE "Company" ADD COLUMN "invoiceEmail" TEXT;

View file

@ -122,6 +122,7 @@ model Company {
telephone String? telephone String?
mobile String? mobile String?
email String? email String?
invoiceEmail String?
invoiceAddress String? invoiceAddress String?
isActive Boolean @default(true) isActive Boolean @default(true)
createdAt DateTime @default(now()) createdAt DateTime @default(now())