feat: add check position selector and fix logo not rendering
Add per-account check position setting (top/middle/bottom/3-per-page) so checks print in a specific slot on the page. Fix logos never appearing on checks or in the layout editor — the Logo layout field was missing from the default seed data and existing accounts.
This commit is contained in:
+6
-3
@@ -106,7 +106,7 @@ app.put('/api/account/:id', requireAdmin, (req, res) => {
|
||||
bank_name, bank_info1, bank_info2, bank_info3, transit_code,
|
||||
routing_number, account_number,
|
||||
offset_left, offset_right, offset_up, offset_down,
|
||||
logo_data, second_signature,
|
||||
logo_data, second_signature, check_position,
|
||||
} = req.body;
|
||||
|
||||
if (!company1 || !routing_number || !account_number) {
|
||||
@@ -117,13 +117,16 @@ app.put('/api/account/:id', requireAdmin, (req, res) => {
|
||||
return res.status(400).json({ error: 'Logo image must be smaller than 512 KB.' });
|
||||
}
|
||||
|
||||
const VALID_POSITIONS = ['3-per-page', 'top', 'middle', 'bottom'];
|
||||
const resolvedPosition = VALID_POSITIONS.includes(check_position) ? check_position : '3-per-page';
|
||||
|
||||
db.prepare(`
|
||||
UPDATE account SET
|
||||
company1 = ?, company2 = ?, company3 = ?, company4 = ?,
|
||||
bank_name = ?, bank_info1 = ?, bank_info2 = ?, bank_info3 = ?, transit_code = ?,
|
||||
routing_number = ?, account_number = ?,
|
||||
offset_left = ?, offset_right = ?, offset_up = ?, offset_down = ?,
|
||||
second_signature = ?,
|
||||
second_signature = ?, check_position = ?,
|
||||
logo_data = CASE WHEN ? IS NOT NULL THEN ? ELSE logo_data END,
|
||||
updated_at = datetime('now')
|
||||
WHERE id = ?
|
||||
@@ -133,7 +136,7 @@ app.put('/api/account/:id', requireAdmin, (req, res) => {
|
||||
routing_number, account_number,
|
||||
parseFloat(offset_left) || 0, parseFloat(offset_right) || 0,
|
||||
parseFloat(offset_up) || 0, parseFloat(offset_down) || 0,
|
||||
second_signature ? 1 : 0,
|
||||
second_signature ? 1 : 0, resolvedPosition,
|
||||
logo_data || null, logo_data || null,
|
||||
req.params.id
|
||||
);
|
||||
|
||||
@@ -160,6 +160,8 @@ db.exec(`
|
||||
|
||||
// Default layout fields used for seeding and migration.
|
||||
const DEFAULT_LAYOUT_FIELDS = [
|
||||
// Logo — top left corner (Graph type, rendered as image from account.logo_data)
|
||||
{ field_name: 'Logo', field_type: 'Graph', x_pos: 0.10, y_pos: 0.08, x_end_pos: 0.45, y_end_pos: 0.58, font_name: 'Helvetica', font_size: 10, font_bold: 0, field_text: null, line_thick: 1, visible: 1 },
|
||||
// Company block — top left
|
||||
{ field_name: 'Company Name', field_type: 'Regular', x_pos: 0.50, y_pos: 0.12, x_end_pos: 0, y_end_pos: 0, font_name: 'Helvetica-Bold', font_size: 10, font_bold: 1, field_text: null, line_thick: 1, visible: 1 },
|
||||
{ field_name: 'Company Name2', field_type: 'Regular', x_pos: 0.50, y_pos: 0.30, x_end_pos: 0, y_end_pos: 0, font_name: 'Helvetica', font_size: 9, font_bold: 0, field_text: null, line_thick: 1, visible: 1 },
|
||||
@@ -227,6 +229,21 @@ if (!db.prepare("SELECT value FROM settings WHERE key = 'layout_reset_v1'").get(
|
||||
})();
|
||||
}
|
||||
|
||||
// Migration: add Logo field to existing accounts that don't have one.
|
||||
(function addLogoField() {
|
||||
const accounts = db.prepare('SELECT id FROM account').all();
|
||||
const insertLogo = db.prepare(`
|
||||
INSERT OR IGNORE INTO layout_fields
|
||||
(account_id, field_name, field_text, font_name, font_size, font_bold,
|
||||
field_type, line_thick, x_pos, y_pos, x_end_pos, y_end_pos, visible)
|
||||
VALUES (?, 'Logo', NULL, 'Helvetica', 10, 0, 'Graph', 1, 0.10, 0.08, 0.45, 0.58, 1)
|
||||
`);
|
||||
for (const { id } of accounts) {
|
||||
const existing = db.prepare("SELECT id FROM layout_fields WHERE account_id = ? AND field_name = 'Logo'").get(id);
|
||||
if (!existing) insertLogo.run(id);
|
||||
}
|
||||
})();
|
||||
|
||||
// Migration: seed default layout fields for any account that has none (ongoing, idempotent).
|
||||
(function seedMissingLayoutFields() {
|
||||
const accounts = db.prepare('SELECT id FROM account').all();
|
||||
|
||||
@@ -131,13 +131,21 @@ function generateCheckPdf(account, checks, fields) {
|
||||
const offX = (account.offset_right - account.offset_left);
|
||||
const offY = (account.offset_down - account.offset_up);
|
||||
|
||||
// Render checks in pages of 3; add a new page for each additional group
|
||||
const pages = Math.ceil(checks.length / 3);
|
||||
// Determine slot assignment based on check_position setting
|
||||
const position = account.check_position || '3-per-page';
|
||||
const SLOT_MAP = { top: 0, middle: 1, bottom: 2 };
|
||||
const fixedSlot = SLOT_MAP[position]; // undefined for '3-per-page'
|
||||
const checksPerPage = fixedSlot !== undefined ? 1 : 3;
|
||||
|
||||
const pages = Math.ceil(checks.length / checksPerPage);
|
||||
for (let page = 0; page < pages; page++) {
|
||||
if (page > 0) doc.addPage();
|
||||
|
||||
for (let slot = 0; slot < 3; slot++) {
|
||||
const check = checks[page * 3 + slot] || null;
|
||||
// For fixed-slot mode, only render in the designated slot
|
||||
if (fixedSlot !== undefined && slot !== fixedSlot) continue;
|
||||
const checkIndex = fixedSlot !== undefined ? page : page * 3 + slot;
|
||||
const check = checks[checkIndex] || null;
|
||||
const slotOriginY = slot * SLOT_HEIGHT_IN;
|
||||
|
||||
// Helper: convert inches (relative to slot) to PDF points (absolute page)
|
||||
|
||||
Reference in New Issue
Block a user