← back to AbramsOS

routes/warranties.js

105 lines

// Warranties — manual entry of warranties / guarantees / return windows.
// Rows land in service_commitment (source='manual'), same table the receipt
// extractor populates, so they share the Deadlines engine + claims flow.
const express = require('express');
const db = require('../lib/db');
const audit = require('../lib/audit');
const { id } = require('../lib/ids');
const { daysUntil } = require('../lib/recurrence');

const router = express.Router();
const DEV_USER_ID = 'user_steve';

const TYPES = ['warranty', 'return', 'guarantee', 'service', 'other'];

function decorate(w) {
  const d = w.refund_window_ends_at ? daysUntil(w.refund_window_ends_at) : null;
  return {
    ...w,
    days_until: d,
    is_expired: d != null && d < 0,
    is_expiring_soon: d != null && d >= 0 && d <= 30,
  };
}

async function listWarranties(userId) {
  const r = await db.query(
    `SELECT * FROM service_commitment
       WHERE user_id = $1
       ORDER BY (refund_window_ends_at IS NULL), refund_window_ends_at ASC, created_at DESC`,
    [userId]
  );
  return r.rows.map(decorate);
}

router.get('/warranties', async (_req, res) => {
  const warranties = await listWarranties(DEV_USER_ID);
  res.render('warranties', { warranties, types: TYPES });
});

router.get('/api/warranties', async (_req, res) => {
  res.json(await listWarranties(DEV_USER_ID));
});

router.post('/api/warranties', async (req, res) => {
  try {
    const b = req.body || {};
    if (!b.provider_name && !b.guarantee_text) return res.status(400).json({ error: 'provider or coverage description required' });
    const wId = id('warranty');
    await db.query(
      `INSERT INTO service_commitment
         (id, user_id, provider_name, commitment_type, guarantee_text, promised_outcome, window_days, refund_window_ends_at, conditions, source, confidence)
       VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,'manual',1.0)`,
      [
        wId, DEV_USER_ID, b.provider_name || null,
        TYPES.includes(b.commitment_type) ? b.commitment_type : 'warranty',
        b.guarantee_text || b.provider_name || 'Warranty',
        b.promised_outcome || null,
        b.window_days ? parseInt(b.window_days, 10) : null,
        b.refund_window_ends_at || null,
        b.conditions || null,
      ]
    );
    await audit.log({ actorType: 'user', actorId: DEV_USER_ID, objectType: 'service_commitment', objectId: wId, eventType: 'warranty_created', metadata: { provider: b.provider_name, type: b.commitment_type } });
    const r = await db.query(`SELECT * FROM service_commitment WHERE id = $1`, [wId]);
    res.status(201).json(decorate(r.rows[0]));
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

router.post('/api/warranties/:id', async (req, res) => {
  try {
    const b = req.body || {};
    const fields = ['provider_name', 'commitment_type', 'guarantee_text', 'promised_outcome', 'window_days', 'refund_window_ends_at', 'conditions'];
    const sets = [];
    const vals = [];
    let i = 1;
    for (const f of fields) {
      if (b[f] === undefined) continue;
      sets.push(`${f} = $${i++}`);
      vals.push(b[f] === '' ? null : b[f]);
    }
    if (!sets.length) return res.status(400).json({ error: 'no fields to update' });
    vals.push(req.params.id, DEV_USER_ID);
    const r = await db.query(
      `UPDATE service_commitment SET ${sets.join(', ')} WHERE id = $${i++} AND user_id = $${i} RETURNING *`,
      vals
    );
    if (!r.rows.length) return res.status(404).json({ error: 'not found' });
    await audit.log({ actorType: 'user', actorId: DEV_USER_ID, objectType: 'service_commitment', objectId: req.params.id, eventType: 'warranty_updated' });
    res.json(decorate(r.rows[0]));
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

router.post('/api/warranties/:id/delete', async (req, res) => {
  await db.query(`DELETE FROM calendar_reminder WHERE user_id = $1 AND owner_table = 'service_commitment' AND owner_id = $2`, [DEV_USER_ID, req.params.id]);
  await db.query(`DELETE FROM service_commitment WHERE id = $1 AND user_id = $2`, [req.params.id, DEV_USER_ID]);
  await audit.log({ actorType: 'user', actorId: DEV_USER_ID, objectType: 'service_commitment', objectId: req.params.id, eventType: 'warranty_deleted' });
  res.json({ ok: true });
});

module.exports = router;