mirror of
https://git.meshkee.com/Meshkee/backend.git
synced 2026-08-11 22:30:59 +04:30
Add platform invoices API with item templates and optional name.
Super admins can manage predefined invoice lines and issue invoices to businesses; schema is ready for future business-scoped use. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
136711dfb3
commit
426316d53c
@@ -0,0 +1,167 @@
|
||||
-- Meshkee CMS — invoices (platform / business billing)
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- enums
|
||||
-- ---------------------------------------------------------------------------
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE invoice_owner_scope AS ENUM ('platform', 'business');
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE invoice_status AS ENUM (
|
||||
'draft',
|
||||
'issued',
|
||||
'paid',
|
||||
'cancelled'
|
||||
);
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- invoice item templates (predefined line items)
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE invoice_item_templates (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
owner_scope invoice_owner_scope NOT NULL,
|
||||
business_id BIGINT,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
duration VARCHAR(100),
|
||||
worktime VARCHAR(100),
|
||||
description TEXT,
|
||||
price NUMERIC(12, 2) NOT NULL DEFAULT 0,
|
||||
discounted_price NUMERIC(12, 2),
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT invoice_item_templates_business_id_fkey
|
||||
FOREIGN KEY (business_id) REFERENCES businesses (id) ON DELETE CASCADE,
|
||||
CONSTRAINT invoice_item_templates_title_nonempty
|
||||
CHECK (char_length(trim(title)) > 0),
|
||||
CONSTRAINT invoice_item_templates_price_non_negative
|
||||
CHECK (price >= 0),
|
||||
CONSTRAINT invoice_item_templates_discounted_non_negative
|
||||
CHECK (discounted_price IS NULL OR discounted_price >= 0),
|
||||
CONSTRAINT invoice_item_templates_scope_business_check
|
||||
CHECK (
|
||||
(owner_scope = 'platform' AND business_id IS NULL)
|
||||
OR (owner_scope = 'business' AND business_id IS NOT NULL)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_invoice_item_templates_owner_scope
|
||||
ON invoice_item_templates (owner_scope, sort_order);
|
||||
CREATE INDEX idx_invoice_item_templates_business_id
|
||||
ON invoice_item_templates (business_id, sort_order)
|
||||
WHERE business_id IS NOT NULL;
|
||||
|
||||
CREATE TRIGGER invoice_item_templates_set_updated_at
|
||||
BEFORE UPDATE ON invoice_item_templates
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- invoices
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE invoices (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
business_id BIGINT NOT NULL,
|
||||
owner_scope invoice_owner_scope NOT NULL DEFAULT 'platform',
|
||||
issuer_business_id BIGINT,
|
||||
status invoice_status NOT NULL DEFAULT 'issued',
|
||||
notes TEXT,
|
||||
issued_by BIGINT,
|
||||
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT invoices_business_id_fkey
|
||||
FOREIGN KEY (business_id) REFERENCES businesses (id) ON DELETE CASCADE,
|
||||
CONSTRAINT invoices_issuer_business_id_fkey
|
||||
FOREIGN KEY (issuer_business_id) REFERENCES businesses (id) ON DELETE SET NULL,
|
||||
CONSTRAINT invoices_issued_by_fkey
|
||||
FOREIGN KEY (issued_by) REFERENCES users (id) ON DELETE SET NULL,
|
||||
CONSTRAINT invoices_scope_issuer_check
|
||||
CHECK (
|
||||
(owner_scope = 'platform' AND issuer_business_id IS NULL)
|
||||
OR (owner_scope = 'business' AND issuer_business_id IS NOT NULL)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_invoices_business_created
|
||||
ON invoices (business_id, created_at DESC);
|
||||
CREATE INDEX idx_invoices_owner_scope
|
||||
ON invoices (owner_scope, created_at DESC);
|
||||
CREATE INDEX idx_invoices_issuer_business_id
|
||||
ON invoices (issuer_business_id, created_at DESC)
|
||||
WHERE issuer_business_id IS NOT NULL;
|
||||
CREATE INDEX idx_invoices_status
|
||||
ON invoices (status);
|
||||
|
||||
CREATE TRIGGER invoices_set_updated_at
|
||||
BEFORE UPDATE ON invoices
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- invoice line items
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE invoice_items (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
invoice_id BIGINT NOT NULL,
|
||||
template_id BIGINT,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
duration VARCHAR(100),
|
||||
worktime VARCHAR(100),
|
||||
description TEXT,
|
||||
price NUMERIC(12, 2) NOT NULL DEFAULT 0,
|
||||
discounted_price NUMERIC(12, 2),
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT invoice_items_invoice_id_fkey
|
||||
FOREIGN KEY (invoice_id) REFERENCES invoices (id) ON DELETE CASCADE,
|
||||
CONSTRAINT invoice_items_template_id_fkey
|
||||
FOREIGN KEY (template_id) REFERENCES invoice_item_templates (id) ON DELETE SET NULL,
|
||||
CONSTRAINT invoice_items_title_nonempty
|
||||
CHECK (char_length(trim(title)) > 0),
|
||||
CONSTRAINT invoice_items_price_non_negative
|
||||
CHECK (price >= 0),
|
||||
CONSTRAINT invoice_items_discounted_non_negative
|
||||
CHECK (discounted_price IS NULL OR discounted_price >= 0)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_invoice_items_invoice_id
|
||||
ON invoice_items (invoice_id, sort_order);
|
||||
CREATE INDEX idx_invoice_items_template_id
|
||||
ON invoice_items (template_id)
|
||||
WHERE template_id IS NOT NULL;
|
||||
|
||||
CREATE TRIGGER invoice_items_set_updated_at
|
||||
BEFORE UPDATE ON invoice_items
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- permissions (for future business dashboard use)
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO permissions (name, slug, group_name, description) VALUES
|
||||
('View invoices', 'invoices.read', 'invoices', 'View invoices'),
|
||||
('Create invoices', 'invoices.create', 'invoices', 'Create invoices'),
|
||||
('Update invoices', 'invoices.update', 'invoices', 'Update invoices'),
|
||||
('Delete invoices', 'invoices.delete', 'invoices', 'Delete invoices'),
|
||||
('View invoice templates', 'invoice_templates.read', 'invoices', 'View invoice item templates'),
|
||||
('Manage invoice templates', 'invoice_templates.manage', 'invoices', 'Create, update, and delete invoice item templates')
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r
|
||||
JOIN permissions p ON p.slug LIKE 'invoices.%' OR p.slug LIKE 'invoice_templates.%'
|
||||
WHERE r.slug IN ('business_owner', 'owner', 'admin')
|
||||
ON CONFLICT DO NOTHING;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Meshkee CMS — optional invoice name
|
||||
|
||||
ALTER TABLE invoices
|
||||
ADD COLUMN IF NOT EXISTS name VARCHAR(255);
|
||||
Reference in New Issue
Block a user