- Progressive Web App with Express + JSON backend - PIN-based auth, multi-device sync - Invoice + Statement PDF generation (branded, Grandstander + Nunito) - Mobile + Desktop responsive layouts - n8n backup integration - Custom logo upload - Settings for business info, fees, banking - Bulk invoice generation - Web Share API integration
219 lines
9 KiB
JavaScript
219 lines
9 KiB
JavaScript
/* Slim Uiltjies — Sample seed data
|
|
Pre-populates the app so it works immediately on first load.
|
|
*/
|
|
(function () {
|
|
'use strict';
|
|
|
|
async function seed({ force = false } = {}) {
|
|
const seeded = await window.SUDB.getMeta('seeded');
|
|
if (seeded && !force) return false;
|
|
|
|
if (force) {
|
|
await window.SUDB.clear('parents');
|
|
await window.SUDB.clear('children');
|
|
await window.SUDB.clear('invoices');
|
|
await window.SUDB.clear('payments');
|
|
await window.SUDB.clear('business');
|
|
}
|
|
|
|
// Business info — pulled from the original receipt
|
|
await window.SUDB.saveBusiness({
|
|
name: 'Slim Uiltjies AfterCare',
|
|
tagline: 'A safe place to learn, grow and shine!',
|
|
address: 'A loving aftercare, a second home.',
|
|
phone: '084 770 7077',
|
|
email: '',
|
|
facebook: 'Slim Uiltjies AfterCare',
|
|
currency: 'R',
|
|
currencyCode: 'ZAR',
|
|
taxNote: '',
|
|
bankName: '',
|
|
accountHolder: '',
|
|
accountNumber: '',
|
|
branchCode: '',
|
|
referenceNote: 'Use child name as payment reference',
|
|
footerNote: "We truly appreciate your timely payments.\nTogether we make a difference in our children's future!",
|
|
receiptPrefix: 'SU',
|
|
nextReceiptNumber: 1,
|
|
defaultFullDayFee: 500,
|
|
defaultHalfDayFee: 300,
|
|
updatedAt: new Date().toISOString(),
|
|
});
|
|
|
|
// Parents
|
|
const parents = [
|
|
{ id: 1, name: 'Valentine', phone: '082 123 4567', email: 'valentine@example.com', address: '12 Acacia Street, Pretoria', notes: '', createdAt: new Date().toISOString() },
|
|
{ id: 2, name: 'Nomsa Dlamini', phone: '073 555 0102', email: 'nomsa@example.com', address: '4 Violet Lane, Centurion', notes: '', createdAt: new Date().toISOString() },
|
|
{ id: 3, name: 'Pieter van Wyk', phone: '083 444 9988', email: 'pieter@example.com', address: '78 Garden Road, Hatfield', notes: '', createdAt: new Date().toISOString() },
|
|
];
|
|
for (const p of parents) await window.SUDB.put('parents', p);
|
|
|
|
// Children
|
|
const children = [
|
|
{ id: 1, parentId: 1, name: 'Tshireletso', plan: 'full', monthlyFee: 500, startDate: '2025-01-01', endDate: '', notes: '', createdAt: new Date().toISOString() },
|
|
{ id: 2, parentId: 2, name: 'Lerato', plan: 'full', monthlyFee: 500, startDate: '2025-02-01', endDate: '', notes: '', createdAt: new Date().toISOString() },
|
|
{ id: 3, parentId: 2, name: 'Themba', plan: 'half', monthlyFee: 300, startDate: '2025-02-01', endDate: '', notes: '', createdAt: new Date().toISOString() },
|
|
{ id: 4, parentId: 3, name: 'Mia', plan: 'full', monthlyFee: 500, startDate: '2025-03-01', endDate: '', notes: '', createdAt: new Date().toISOString() },
|
|
];
|
|
for (const c of children) await window.SUDB.put('children', c);
|
|
|
|
// Historical invoices (mirroring the original receipt's proof of payments)
|
|
const now = new Date();
|
|
const year = now.getFullYear();
|
|
const months = [
|
|
`${year}-01`, `${year}-02`, `${year}-03`, `${year}-04`, `${year}-05`, `${year}-06`
|
|
];
|
|
let invId = 1;
|
|
let payId = 1;
|
|
|
|
// Build the same payment pattern as the receipt for Tshireletso
|
|
// March is missing on the original receipt — left unpaid (no payment record)
|
|
const tsInvoices = [
|
|
{ period: `${year}-01`, day: 7, amount: 500, method: 'cash', by: 'Syl-Marie' },
|
|
{ period: `${year}-02`, day: 25, amount: 500, method: 'cash', by: 'Syl-Marie' },
|
|
{ period: `${year}-03`, day: 0, amount: 0, method: null, by: null },
|
|
{ period: `${year}-04`, day: 24, amount: 1000, method: 'cash', by: 'Syl-Marie' },
|
|
{ period: `${year}-05`, day: 23, amount: 500, method: 'cash', by: 'Syl-Marie' },
|
|
{ period: `${year}-06`, day: 29, amount: 500, method: 'cash', by: 'Syl-Marie' },
|
|
];
|
|
|
|
for (const item of tsInvoices) {
|
|
const [yy, mm] = item.period.split('-').map(Number);
|
|
const issueDate = `${yy}-${String(mm).padStart(2, '0')}-05`;
|
|
const dueDate = `${yy}-${String(mm).padStart(2, '0')}-25`;
|
|
const inv = {
|
|
id: invId++,
|
|
number: `SU-${String(invId - 1).padStart(4, '0')}`,
|
|
parentId: 1,
|
|
childId: 1,
|
|
period: item.period,
|
|
issueDate,
|
|
dueDate,
|
|
items: [{ description: `AfterCare Fees — ${formatPeriod(item.period)}`, quantity: 1, unitPrice: 500, total: 500 }],
|
|
subtotal: 500,
|
|
total: 500,
|
|
paid: item.amount,
|
|
balance: Math.max(0, 500 - item.amount),
|
|
status: item.amount >= 500 ? 'paid' : (item.amount > 0 ? 'partial' : 'unpaid'),
|
|
notes: item.amount === 1000 ? 'March + April combined payment' : '',
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
await window.SUDB.put('invoices', inv);
|
|
|
|
if (item.amount > 0) {
|
|
const payDate = `${yy}-${String(mm).padStart(2, '0')}-${String(item.day).padStart(2, '0')}`;
|
|
await window.SUDB.put('payments', {
|
|
id: payId++,
|
|
invoiceId: inv.id,
|
|
date: payDate,
|
|
amount: item.amount,
|
|
method: item.method,
|
|
receivedBy: item.by,
|
|
notes: '',
|
|
createdAt: new Date().toISOString(),
|
|
});
|
|
}
|
|
}
|
|
|
|
// Add an unpaid invoice for Lerato this month
|
|
const currentPeriod = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
|
|
const [yy2, mm2] = currentPeriod.split('-').map(Number);
|
|
const lerInv = {
|
|
id: invId++,
|
|
number: `SU-${String(invId - 1).padStart(4, '0')}`,
|
|
parentId: 2,
|
|
childId: 2,
|
|
period: currentPeriod,
|
|
issueDate: `${yy2}-${String(mm2).padStart(2, '0')}-01`,
|
|
dueDate: `${yy2}-${String(mm2).padStart(2, '0')}-25`,
|
|
items: [{ description: `AfterCare Fees — ${formatPeriod(currentPeriod)}`, quantity: 1, unitPrice: 500, total: 500 }],
|
|
subtotal: 500, total: 500, paid: 0, balance: 500,
|
|
status: 'unpaid', notes: '', createdAt: new Date().toISOString(),
|
|
};
|
|
await window.SUDB.put('invoices', lerInv);
|
|
|
|
// Paid invoice for Lerato previous month
|
|
const prevDate = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
|
const prevPeriod = `${prevDate.getFullYear()}-${String(prevDate.getMonth() + 1).padStart(2, '0')}`;
|
|
const lerPrev = {
|
|
id: invId++,
|
|
number: `SU-${String(invId - 1).padStart(4, '0')}`,
|
|
parentId: 2,
|
|
childId: 2,
|
|
period: prevPeriod,
|
|
issueDate: `${prevPeriod}-01`,
|
|
dueDate: `${prevPeriod}-25`,
|
|
items: [{ description: `AfterCare Fees — ${formatPeriod(prevPeriod)}`, quantity: 1, unitPrice: 500, total: 500 }],
|
|
subtotal: 500, total: 500, paid: 500, balance: 0,
|
|
status: 'paid', notes: '', createdAt: new Date().toISOString(),
|
|
};
|
|
await window.SUDB.put('invoices', lerPrev);
|
|
await window.SUDB.put('payments', {
|
|
id: payId++,
|
|
invoiceId: lerPrev.id,
|
|
date: `${prevPeriod}-15`,
|
|
amount: 500,
|
|
method: 'eft',
|
|
receivedBy: 'Syl-Marie',
|
|
notes: '',
|
|
createdAt: new Date().toISOString(),
|
|
});
|
|
|
|
// Mia — paid current month
|
|
const miaInv = {
|
|
id: invId++,
|
|
number: `SU-${String(invId - 1).padStart(4, '0')}`,
|
|
parentId: 3,
|
|
childId: 4,
|
|
period: currentPeriod,
|
|
issueDate: `${yy2}-${String(mm2).padStart(2, '0')}-01`,
|
|
dueDate: `${yy2}-${String(mm2).padStart(2, '0')}-25`,
|
|
items: [{ description: `AfterCare Fees — ${formatPeriod(currentPeriod)}`, quantity: 1, unitPrice: 500, total: 500 }],
|
|
subtotal: 500, total: 500, paid: 500, balance: 0,
|
|
status: 'paid', notes: '', createdAt: new Date().toISOString(),
|
|
};
|
|
await window.SUDB.put('invoices', miaInv);
|
|
await window.SUDB.put('payments', {
|
|
id: payId++,
|
|
invoiceId: miaInv.id,
|
|
date: `${yy2}-${String(mm2).padStart(2, '0')}-10`,
|
|
amount: 500, method: 'cash', receivedBy: 'Syl-Marie',
|
|
notes: '', createdAt: new Date().toISOString(),
|
|
});
|
|
|
|
// Themba — paid half-day fees
|
|
const thembaInv = {
|
|
id: invId++,
|
|
number: `SU-${String(invId - 1).padStart(4, '0')}`,
|
|
parentId: 2,
|
|
childId: 3,
|
|
period: currentPeriod,
|
|
issueDate: `${yy2}-${String(mm2).padStart(2, '0')}-01`,
|
|
dueDate: `${yy2}-${String(mm2).padStart(2, '0')}-25`,
|
|
items: [{ description: `AfterCare Fees (Half Day) — ${formatPeriod(currentPeriod)}`, quantity: 1, unitPrice: 300, total: 300 }],
|
|
subtotal: 300, total: 300, paid: 300, balance: 0,
|
|
status: 'paid', notes: '', createdAt: new Date().toISOString(),
|
|
};
|
|
await window.SUDB.put('invoices', thembaInv);
|
|
await window.SUDB.put('payments', {
|
|
id: payId++,
|
|
invoiceId: thembaInv.id,
|
|
date: `${yy2}-${String(mm2).padStart(2, '0')}-12`,
|
|
amount: 300, method: 'eft', receivedBy: 'Syl-Marie',
|
|
notes: '', createdAt: new Date().toISOString(),
|
|
});
|
|
|
|
await window.SUDB.setMeta('seeded', true);
|
|
await window.SUDB.setMeta('seededAt', new Date().toISOString());
|
|
await window.SUDB.setMeta('nextReceipt', invId);
|
|
return true;
|
|
}
|
|
|
|
function formatPeriod(p) {
|
|
const [y, m] = p.split('-').map(Number);
|
|
const months = ['January','February','March','April','May','June','July','August','September','October','November','December'];
|
|
return `${months[m - 1]} ${y}`;
|
|
}
|
|
|
|
window.SUSeed = { seed };
|
|
})();
|