export const INIT_META = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'meta') BEGIN CREATE TABLE meta ( [key] NVARCHAR(255) NOT NULL, value NVARCHAR(MAX) NOT NULL, CONSTRAINT PK_meta PRIMARY KEY ([key]) ); END IF NOT EXISTS (SELECT 1 FROM meta WHERE [key] = N'version') BEGIN INSERT INTO meta ([key], value) VALUES (N'version', N'1'); END `; export const INIT_GOOGLE_AUTH = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'google_auth') BEGIN CREATE TABLE google_auth ( id INT IDENTITY(1,1) PRIMARY KEY, email NVARCHAR(320) NOT NULL, picture NVARCHAR(2048) NULL, picturebinary VARBINARY(MAX) NULL, sub NVARCHAR(255) NULL, created_at DATETIME2 NOT NULL CONSTRAINT DF_google_auth_created_at DEFAULT SYSUTCDATETIME(), selected_company INT NULL, CONSTRAINT UQ_google_auth_email UNIQUE (email), CONSTRAINT FK_google_auth_selected_company FOREIGN KEY (selected_company) REFERENCES company(id) ON DELETE SET NULL ); CREATE UNIQUE INDEX UQ_google_auth_sub ON google_auth(sub) WHERE sub IS NOT NULL; END `; export const INIT_LANGUAGE = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'language') BEGIN CREATE TABLE language ( id INT IDENTITY(1,1) PRIMARY KEY, iso639 NVARCHAR(10) NOT NULL, CONSTRAINT UQ_language_iso639 UNIQUE (iso639) ); END `; export const INIT_TAX = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'tax') BEGIN CREATE TABLE tax ( id INT IDENTITY(1,1) PRIMARY KEY, rate DECIMAL(18,4) NOT NULL, CONSTRAINT UQ_tax_rate UNIQUE (rate) ); END IF NOT EXISTS (SELECT 1 FROM tax WHERE rate = 0) BEGIN INSERT INTO tax (rate) VALUES (0); END IF NOT EXISTS (SELECT 1 FROM tax WHERE rate = 7) BEGIN INSERT INTO tax (rate) VALUES (7); END IF NOT EXISTS (SELECT 1 FROM tax WHERE rate = 19) BEGIN INSERT INTO tax (rate) VALUES (19); END `; export const INIT_CATEGORY = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'category') BEGIN CREATE TABLE category ( id INT IDENTITY(1,1) PRIMARY KEY, parent_id INT NULL, sort INT NOT NULL CONSTRAINT DF_category_sort DEFAULT 0, CONSTRAINT FK_category_parent FOREIGN KEY (parent_id) REFERENCES category(id) ON DELETE NO ACTION ); END `; export const INIT_PIC = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'pic') BEGIN CREATE TABLE pic ( id INT IDENTITY(1,1) PRIMARY KEY, w INT NOT NULL, h INT NOT NULL, size BIGINT NOT NULL, hash NVARCHAR(64) NOT NULL, thumbw INT NOT NULL, thumbh INT NOT NULL, thumbhash NVARCHAR(64) NOT NULL, source NVARCHAR(MAX) NOT NULL, fullbinary VARBINARY(MAX) NOT NULL, thumbbinary VARBINARY(MAX) NOT NULL, CONSTRAINT UQ_pic_hash UNIQUE (hash) ); END `; export const INIT_COMPANY = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'company') BEGIN CREATE TABLE company ( id INT IDENTITY(1,1) PRIMARY KEY, name NVARCHAR(255) NOT NULL, root_category_id INT NULL, default_language_id INT NULL, taxrate INT NULL, reducedtaxrate INT NULL, taxfreerate INT NULL, poscustomergroup_id INT NOT NULL, CONSTRAINT UQ_company_name UNIQUE (name), CONSTRAINT FK_company_root_category FOREIGN KEY (root_category_id) REFERENCES category(id) ON DELETE SET NULL, CONSTRAINT FK_company_default_language FOREIGN KEY (default_language_id) REFERENCES language(id) ON DELETE SET NULL, CONSTRAINT FK_company_taxrate FOREIGN KEY (taxrate) REFERENCES tax(id) ON DELETE NO ACTION, CONSTRAINT FK_company_reducedtaxrate FOREIGN KEY (reducedtaxrate) REFERENCES tax(id) ON DELETE NO ACTION, CONSTRAINT FK_company_taxfreerate FOREIGN KEY (taxfreerate) REFERENCES tax(id) ON DELETE NO ACTION ); END `; export const INIT_CUSTOMERGROUP = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'customergroup') BEGIN CREATE TABLE customergroup ( id INT IDENTITY(1,1) PRIMARY KEY, name NVARCHAR(255) NOT NULL, sort INT NOT NULL CONSTRAINT DF_customergroup_sort DEFAULT 0, -- Nullable only for company-create chicken-egg (POS group first, then company, then SET). -- Always set before commit; never left null for live rows. company_id INT NULL, CONSTRAINT FK_customergroup_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE ); END `; export const INIT_COMPANY_POS_CUSTOMERGROUP_FK = ` IF NOT EXISTS ( SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_company_poscustomergroup' ) BEGIN ALTER TABLE company ADD CONSTRAINT FK_company_poscustomergroup FOREIGN KEY (poscustomergroup_id) REFERENCES customergroup(id) ON DELETE NO ACTION; END `; export const INIT_ENTITY = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'entity') BEGIN CREATE TABLE entity ( id INT IDENTITY(1,1) PRIMARY KEY, customergroup_id INT NOT NULL, first_name NVARCHAR(255) NULL, name NVARCHAR(255) NOT NULL, [number] NVARCHAR(100) NULL, created_at DATETIME2 NOT NULL CONSTRAINT DF_entity_created_at DEFAULT SYSUTCDATETIME(), updated_at DATETIME2 NOT NULL CONSTRAINT DF_entity_updated_at DEFAULT SYSUTCDATETIME(), CONSTRAINT FK_entity_customergroup FOREIGN KEY (customergroup_id) REFERENCES customergroup(id) ON DELETE CASCADE ); END `; /** Unique (company_id, number) for non-null entity numbers via customergroup. */ export const INIT_ENTITY_COMPANY_NUMBER_UQ = ` IF OBJECT_ID(N'dbo.v_entity_company_number', N'V') IS NULL BEGIN EXEC(' CREATE VIEW dbo.v_entity_company_number WITH SCHEMABINDING AS SELECT e.id AS entity_id, cg.company_id, e.[number] FROM dbo.entity AS e INNER JOIN dbo.customergroup AS cg ON cg.id = e.customergroup_id WHERE e.[number] IS NOT NULL '); END IF NOT EXISTS ( SELECT 1 FROM sys.indexes WHERE name = N'UQ_entity_company_number' AND object_id = OBJECT_ID(N'dbo.v_entity_company_number') ) BEGIN CREATE UNIQUE CLUSTERED INDEX UQ_entity_company_number ON dbo.v_entity_company_number (company_id, [number]); END `; export const INIT_ENTITYADDRESS = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'entityaddress') BEGIN CREATE TABLE entityaddress ( id INT IDENTITY(1,1) PRIMARY KEY, entity_id INT NOT NULL, first_name NVARCHAR(255) NULL, name NVARCHAR(255) NULL, street NVARCHAR(255) NULL, streetnumber NVARCHAR(50) NULL, zip NVARCHAR(50) NULL, city NVARCHAR(255) NULL, county NVARCHAR(255) NULL, addresstype NVARCHAR(50) NOT NULL, sort INT NOT NULL CONSTRAINT DF_entityaddress_sort DEFAULT 0, CONSTRAINT FK_entityaddress_entity FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE CASCADE ); END `; export const INIT_ENTITYCONTACT = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'entitycontact') BEGIN CREATE TABLE entitycontact ( id INT IDENTITY(1,1) PRIMARY KEY, entity_id INT NOT NULL, contacttype NVARCHAR(50) NOT NULL, contact NVARCHAR(255) NOT NULL, sort INT NOT NULL CONSTRAINT DF_entitycontact_sort DEFAULT 0, CONSTRAINT FK_entitycontact_entity FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE CASCADE ); END `; export const INIT_ENTITYHISTORY = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'entityhistory') BEGIN CREATE TABLE entityhistory ( id INT IDENTITY(1,1) PRIMARY KEY, entity_id INT NOT NULL, companyuser_id INT NULL, [date] DATETIME2 NOT NULL CONSTRAINT DF_entityhistory_date DEFAULT SYSUTCDATETIME(), [text] NVARCHAR(MAX) NOT NULL, CONSTRAINT FK_entityhistory_entity FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE CASCADE, CONSTRAINT FK_entityhistory_companyuser FOREIGN KEY (companyuser_id) REFERENCES companyuser(id) ON DELETE NO ACTION ); END `; export const INIT_UNIT = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'unit') BEGIN CREATE TABLE unit ( id INT IDENTITY(1,1) PRIMARY KEY, company_id INT NOT NULL, si NVARCHAR(50) NOT NULL, basesi NVARCHAR(50) NOT NULL, basesifactor FLOAT NOT NULL, CONSTRAINT UQ_unit_company_si UNIQUE (company_id, si), CONSTRAINT FK_unit_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE NO ACTION ); END `; export const INIT_UNITTEXT = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'unittext') BEGIN CREATE TABLE unittext ( id INT IDENTITY(1,1) PRIMARY KEY, unit_id INT NOT NULL, language_id INT NOT NULL, symbol NVARCHAR(50) NOT NULL, name NVARCHAR(255) NOT NULL, CONSTRAINT FK_unittext_unit FOREIGN KEY (unit_id) REFERENCES unit(id) ON DELETE CASCADE, CONSTRAINT FK_unittext_language FOREIGN KEY (language_id) REFERENCES language(id) ON DELETE CASCADE, CONSTRAINT UQ_unittext_unit_language UNIQUE (unit_id, language_id) ); END `; export const INIT_OPTIONSLIST = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'optionslist') BEGIN CREATE TABLE optionslist ( id INT IDENTITY(1,1) PRIMARY KEY ); END `; export const INIT_ARTICLELIST = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'articlelist') BEGIN CREATE TABLE articlelist ( id INT IDENTITY(1,1) PRIMARY KEY ); END `; export const INIT_BRAND = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'brand') BEGIN CREATE TABLE brand ( id INT IDENTITY(1,1) PRIMARY KEY, company_id INT NOT NULL, name NVARCHAR(255) NOT NULL, gsprpinfo NVARCHAR(MAX) NULL, CONSTRAINT UQ_brand_company_name UNIQUE (company_id, name), CONSTRAINT FK_brand_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE NO ACTION ); END `; export const INIT_ARTICLE = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'article') BEGIN CREATE TABLE article ( id INT IDENTITY(1,1) PRIMARY KEY, company_id INT NOT NULL, sku NVARCHAR(100) NULL, unit_id INT NOT NULL, unitamount FLOAT NOT NULL, weight FLOAT NOT NULL, track_stock BIT NOT NULL CONSTRAINT DF_article_track_stock DEFAULT 1, brand_id INT NULL, articlelist_id INT NULL, optionslist_id INT NULL, is_father BIT NOT NULL CONSTRAINT DF_article_is_father DEFAULT 0, fatherarticle_id INT NULL, fathersort INT NOT NULL CONSTRAINT DF_article_fathersort DEFAULT 0, created_at DATETIME2 NOT NULL CONSTRAINT DF_article_created_at DEFAULT SYSUTCDATETIME(), updated_at DATETIME2 NOT NULL CONSTRAINT DF_article_updated_at DEFAULT SYSUTCDATETIME(), CONSTRAINT FK_article_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE NO ACTION, CONSTRAINT FK_article_unit FOREIGN KEY (unit_id) REFERENCES unit(id) ON DELETE NO ACTION, CONSTRAINT FK_article_brand FOREIGN KEY (brand_id) REFERENCES brand(id) ON DELETE SET NULL, CONSTRAINT FK_article_articlelist FOREIGN KEY (articlelist_id) REFERENCES articlelist(id) ON DELETE SET NULL, CONSTRAINT FK_article_optionslist FOREIGN KEY (optionslist_id) REFERENCES optionslist(id) ON DELETE SET NULL, CONSTRAINT FK_article_fatherarticle FOREIGN KEY (fatherarticle_id) REFERENCES article(id) ON DELETE NO ACTION, CONSTRAINT UQ_article_company_sku UNIQUE (company_id, sku) ); -- Only enforce sibling sort uniqueness among children of the same father. -- (SQL Server UNIQUE treats NULLs as equal, which would block multiple root articles.) CREATE UNIQUE INDEX UQ_article_fatherarticle_fathersort ON article (fatherarticle_id, fathersort) WHERE fatherarticle_id IS NOT NULL; END ELSE IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('article') AND name = 'track_stock') BEGIN ALTER TABLE article ADD track_stock BIT NOT NULL CONSTRAINT DF_article_track_stock DEFAULT 1; END `; export const INIT_ARTICLEHISTORY = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'articlehistory') BEGIN CREATE TABLE articlehistory ( id INT IDENTITY(1,1) PRIMARY KEY, article_id INT NOT NULL, companyuser_id INT NULL, [date] DATETIME2 NOT NULL CONSTRAINT DF_articlehistory_date DEFAULT SYSUTCDATETIME(), [text] NVARCHAR(MAX) NOT NULL, CONSTRAINT FK_articlehistory_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE, CONSTRAINT FK_articlehistory_companyuser FOREIGN KEY (companyuser_id) REFERENCES companyuser(id) ON DELETE SET NULL ); END `; export const INIT_ARTICLEEAN = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'articleean') BEGIN CREATE TABLE articleean ( id INT IDENTITY(1,1) PRIMARY KEY, article_id INT NOT NULL, ean NVARCHAR(50) NOT NULL, CONSTRAINT FK_articleean_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE, CONSTRAINT UQ_articleean_ean UNIQUE (ean) ); -- Hot path: getBySku EANs by article (UQ is ean-leading only) CREATE INDEX IX_articleean_article_ean ON articleean (article_id, ean); END `; export const INIT_ARTICLEPIC = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'articlepic') BEGIN CREATE TABLE articlepic ( id INT IDENTITY(1,1) PRIMARY KEY, article_id INT NOT NULL, pic_id INT NOT NULL, sort INT NOT NULL CONSTRAINT DF_articlepic_sort DEFAULT 0, CONSTRAINT FK_articlepic_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE, CONSTRAINT FK_articlepic_pic FOREIGN KEY (pic_id) REFERENCES pic(id) ON DELETE CASCADE, CONSTRAINT UQ_articlepic_article_pic UNIQUE (article_id, pic_id), CONSTRAINT UQ_articlepic_article_sort UNIQUE (article_id, sort) ); END `; export const INIT_BRANDPIC = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'brandpic') BEGIN CREATE TABLE brandpic ( id INT IDENTITY(1,1) PRIMARY KEY, brand_id INT NOT NULL, pic_id INT NOT NULL, sort INT NOT NULL CONSTRAINT DF_brandpic_sort DEFAULT 0, CONSTRAINT FK_brandpic_brand FOREIGN KEY (brand_id) REFERENCES brand(id) ON DELETE CASCADE, CONSTRAINT FK_brandpic_pic FOREIGN KEY (pic_id) REFERENCES pic(id) ON DELETE CASCADE, CONSTRAINT UQ_brandpic_brand_pic UNIQUE (brand_id, pic_id), CONSTRAINT UQ_brandpic_brand_sort UNIQUE (brand_id, sort) ); END `; export const INIT_ARTICLELISTARTICLE = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'articlelistarticle') BEGIN CREATE TABLE articlelistarticle ( id INT IDENTITY(1,1) PRIMARY KEY, articlelist_id INT NOT NULL, article_id INT NOT NULL, count INT NOT NULL CONSTRAINT DF_articlelistarticle_count DEFAULT 1, sort INT NOT NULL CONSTRAINT DF_articlelistarticle_sort DEFAULT 0, CONSTRAINT FK_articlelistarticle_articlelist FOREIGN KEY (articlelist_id) REFERENCES articlelist(id) ON DELETE CASCADE, CONSTRAINT FK_articlelistarticle_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE, CONSTRAINT UQ_articlelistarticle_articlelist_article UNIQUE (articlelist_id, article_id), CONSTRAINT UQ_articlelistarticle_articlelist_sort UNIQUE (articlelist_id, sort) ); END `; export const INIT_OPTIONSLISTARTICLE = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'optionslistarticle') BEGIN CREATE TABLE optionslistarticle ( id INT IDENTITY(1,1) PRIMARY KEY, optionslist_id INT NOT NULL, article_id INT NOT NULL, count INT NOT NULL CONSTRAINT DF_optionslistarticle_count DEFAULT 1, sort INT NOT NULL CONSTRAINT DF_optionslistarticle_sort DEFAULT 0, mandated BIT NOT NULL CONSTRAINT DF_optionslistarticle_mandated DEFAULT 0, CONSTRAINT FK_optionslistarticle_optionslist FOREIGN KEY (optionslist_id) REFERENCES optionslist(id) ON DELETE CASCADE, CONSTRAINT FK_optionslistarticle_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE, CONSTRAINT UQ_optionslistarticle_optionslist_article UNIQUE (optionslist_id, article_id), CONSTRAINT UQ_optionslistarticle_optionslist_sort UNIQUE (optionslist_id, sort) ); END `; export const INIT_CATEGORYARTICLE = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'categoryarticle') BEGIN CREATE TABLE categoryarticle ( id INT IDENTITY(1,1) PRIMARY KEY, category_id INT NOT NULL, article_id INT NOT NULL, CONSTRAINT FK_categoryarticle_category FOREIGN KEY (category_id) REFERENCES category(id) ON DELETE NO ACTION, CONSTRAINT FK_categoryarticle_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE, CONSTRAINT UQ_categoryarticle_category_article UNIQUE (category_id, article_id) ); -- Hot path: getBySku / resolve by article (UQ is category-leading) CREATE INDEX IX_categoryarticle_article_category ON categoryarticle (article_id, category_id); END `; export const INIT_CATEGORYPIC = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'categorypic') BEGIN CREATE TABLE categorypic ( id INT IDENTITY(1,1) PRIMARY KEY, category_id INT NOT NULL, pic_id INT NOT NULL, sort INT NOT NULL CONSTRAINT DF_categorypic_sort DEFAULT 0, CONSTRAINT FK_categorypic_category FOREIGN KEY (category_id) REFERENCES category(id) ON DELETE CASCADE, CONSTRAINT FK_categorypic_pic FOREIGN KEY (pic_id) REFERENCES pic(id) ON DELETE CASCADE, CONSTRAINT UQ_categorypic_category_pic UNIQUE (category_id, pic_id), CONSTRAINT UQ_categorypic_category_sort UNIQUE (category_id, sort) ); END `; export const INIT_CATEGORYTEXT = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'categorytext') BEGIN CREATE TABLE categorytext ( id INT IDENTITY(1,1) PRIMARY KEY, category_id INT NOT NULL, language_id INT NOT NULL, name NVARCHAR(255) NOT NULL, slug NVARCHAR(255) NOT NULL, seo NVARCHAR(MAX) NULL, [desc] NVARCHAR(MAX) NULL, CONSTRAINT FK_categorytext_category FOREIGN KEY (category_id) REFERENCES category(id) ON DELETE CASCADE, CONSTRAINT FK_categorytext_language FOREIGN KEY (language_id) REFERENCES language(id) ON DELETE CASCADE, CONSTRAINT UQ_categorytext_category_language UNIQUE (category_id, language_id) ); END `; export const INIT_ARTICLENAME = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'articletext') BEGIN CREATE TABLE articletext ( id INT IDENTITY(1,1) PRIMARY KEY, article_id INT NOT NULL, language_id INT NOT NULL, name NVARCHAR(255) NOT NULL, slug NVARCHAR(255) NOT NULL, seo NVARCHAR(MAX) NULL, [desc] NVARCHAR(MAX) NULL, CONSTRAINT FK_articletext_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE, CONSTRAINT FK_articletext_language FOREIGN KEY (language_id) REFERENCES language(id) ON DELETE CASCADE, CONSTRAINT UQ_articletext_article_language UNIQUE (article_id, language_id) ); END `; export const INIT_PRICE = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'price') BEGIN CREATE TABLE price ( id INT IDENTITY(1,1) PRIMARY KEY, tax_id INT NOT NULL, article_id INT NOT NULL, minmultiplier DECIMAL(18,4) NOT NULL CONSTRAINT DF_price_minmultiplier DEFAULT 1, customergroup_id INT NOT NULL, net FLOAT NOT NULL, CONSTRAINT FK_price_tax FOREIGN KEY (tax_id) REFERENCES tax(id) ON DELETE CASCADE, CONSTRAINT FK_price_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE, CONSTRAINT FK_price_customergroup FOREIGN KEY (customergroup_id) REFERENCES customergroup(id) ON DELETE CASCADE ); -- Hot path: getBySku prices by article (covers ORDER BY + SELECT cols) CREATE INDEX IX_price_article_cg_mult ON price (article_id, customergroup_id, minmultiplier) INCLUDE (net, tax_id); END `; export const INIT_ARTICLE_SUPPLIER = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'articlesupplier') BEGIN CREATE TABLE articlesupplier ( id INT IDENTITY(1,1) PRIMARY KEY, article_id INT NOT NULL, entity_id INT NOT NULL, price_id INT NULL, sku NVARCHAR(100) NULL, stock FLOAT NULL, pu FLOAT NOT NULL CONSTRAINT DF_articlesupplier_pu DEFAULT 1, sort INT NOT NULL CONSTRAINT DF_articlesupplier_sort DEFAULT 0, CONSTRAINT FK_articlesupplier_article FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE, CONSTRAINT FK_articlesupplier_entity FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE CASCADE, CONSTRAINT FK_articlesupplier_price FOREIGN KEY (price_id) REFERENCES price(id) ON DELETE NO ACTION, CONSTRAINT UQ_articlesupplier_article_entity UNIQUE (article_id, entity_id), CONSTRAINT UQ_articlesupplier_article_sort UNIQUE (article_id, sort) ); CREATE INDEX IX_articlesupplier_article_entity ON articlesupplier (article_id, entity_id); END `; export const INIT_MANIFEST = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'manifest') BEGIN CREATE TABLE manifest ( id INT IDENTITY(1,1) PRIMARY KEY, company_id INT NOT NULL, [number] NVARCHAR(100) NOT NULL, [type] NVARCHAR(100) NOT NULL, entity_id INT NULL, external_number NVARCHAR(100) NULL, [date] DATETIME2 NOT NULL CONSTRAINT DF_manifest_date DEFAULT SYSUTCDATETIME(), created_at DATETIME2 NOT NULL CONSTRAINT DF_manifest_created_at DEFAULT SYSUTCDATETIME(), CONSTRAINT FK_manifest_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE NO ACTION, CONSTRAINT FK_manifest_entity FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE SET NULL ); END `; export const INIT_MANIFESTARTICLE = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'manifestarticle') BEGIN CREATE TABLE manifestarticle ( id INT IDENTITY(1,1) PRIMARY KEY, manifest_id INT NOT NULL, sku NVARCHAR(100) NULL, name NVARCHAR(255) NULL, pu FLOAT NOT NULL CONSTRAINT DF_manifestarticle_pu DEFAULT 1, amount FLOAT NOT NULL, net FLOAT NOT NULL, comment NVARCHAR(MAX) NULL, rebate FLOAT NOT NULL CONSTRAINT DF_manifestarticle_rebate DEFAULT 0, taxrate FLOAT NOT NULL CONSTRAINT DF_manifestarticle_taxrate DEFAULT 0, CONSTRAINT FK_manifestarticle_manifest FOREIGN KEY (manifest_id) REFERENCES manifest(id) ON DELETE CASCADE ); END `; export const INIT_WAREHOUSE = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'warehouse') BEGIN CREATE TABLE warehouse ( id INT IDENTITY(1,1) PRIMARY KEY, company_id INT NOT NULL, name NVARCHAR(255) NOT NULL, sort INT NOT NULL CONSTRAINT DF_warehouse_sort DEFAULT 0, CONSTRAINT FK_warehouse_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE, CONSTRAINT UQ_warehouse_company_name UNIQUE (company_id, name) ); END ELSE IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('warehouse') AND name = 'sort') BEGIN ALTER TABLE warehouse ADD sort INT NOT NULL CONSTRAINT DF_warehouse_sort DEFAULT 0; END `; export const INIT_LEDGER = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'ledger') BEGIN CREATE TABLE ledger ( id INT IDENTITY(1,1) PRIMARY KEY, manifestarticle_id INT NOT NULL, warehouse_id INT NULL, transaction_type NVARCHAR(100) NOT NULL, amount FLOAT NOT NULL, transaction_text NVARCHAR(MAX) NOT NULL, hash_version INT NOT NULL, prev_hash NVARCHAR(128) NULL, created_at DATETIME2 NOT NULL CONSTRAINT DF_ledger_created_at DEFAULT SYSUTCDATETIME(), CONSTRAINT FK_ledger_manifestarticle FOREIGN KEY (manifestarticle_id) REFERENCES manifestarticle(id) ON DELETE NO ACTION, CONSTRAINT FK_ledger_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouse(id) ON DELETE NO ACTION ) WITH (LEDGER = ON (APPEND_ONLY = ON)); END `; export const INIT_NUMBERS = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'numbers') BEGIN CREATE TABLE numbers ( id INT IDENTITY(1,1) PRIMARY KEY, company_id INT NOT NULL, [type] NVARCHAR(100) NOT NULL, prefix NVARCHAR(50) NOT NULL CONSTRAINT DF_numbers_prefix DEFAULT '', padding INT NOT NULL CONSTRAINT DF_numbers_padding DEFAULT 0, [next] INT NOT NULL CONSTRAINT DF_numbers_next DEFAULT 1, CONSTRAINT FK_numbers_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE, CONSTRAINT UQ_numbers_company_type UNIQUE (company_id, [type]) ); END `; // EXEC dbo.getnextnumberwithprefixandpadding // @company_id = 1, @type = N'articlesku', @value = @sku OUTPUT; export const INIT_GET_NEXT_NUMBER_WITH_PREFIX_AND_PADDING = ` IF OBJECT_ID(N'dbo.getnextnumberwithprefixandpadding', N'P') IS NOT NULL DROP PROCEDURE dbo.getnextnumberwithprefixandpadding; EXEC(' CREATE PROCEDURE dbo.getnextnumberwithprefixandpadding @company_id INT, @type NVARCHAR(100), @value NVARCHAR(100) OUTPUT AS BEGIN SET NOCOUNT ON; DECLARE @seq TABLE (prefix NVARCHAR(50), padding INT, num INT); UPDATE n SET [next] = [next] + 1 OUTPUT INSERTED.prefix, INSERTED.padding, DELETED.[next] INTO @seq(prefix, padding, num) FROM numbers n WITH (UPDLOCK, ROWLOCK) WHERE n.company_id = @company_id AND n.[type] = @type; IF @@ROWCOUNT = 0 THROW 50000, ''Number type not found for company'', 1; SELECT @value = prefix + CASE WHEN padding > 0 THEN RIGHT(REPLICATE(''0'', padding) + CAST(num AS NVARCHAR(20)), padding) ELSE CAST(num AS NVARCHAR(20)) END FROM @seq; END '); `; // EXEC dbo.peeknextnumberwithprefixandpadding // @company_id = 1, @type = N'entity', @value = @n OUTPUT; export const INIT_PEEK_NEXT_NUMBER_WITH_PREFIX_AND_PADDING = ` IF OBJECT_ID(N'dbo.peeknextnumberwithprefixandpadding', N'P') IS NOT NULL DROP PROCEDURE dbo.peeknextnumberwithprefixandpadding; EXEC(' CREATE PROCEDURE dbo.peeknextnumberwithprefixandpadding @company_id INT, @type NVARCHAR(100), @value NVARCHAR(100) OUTPUT AS BEGIN SET NOCOUNT ON; DECLARE @prefix NVARCHAR(50); DECLARE @padding INT; DECLARE @num INT; SELECT @prefix = n.prefix, @padding = n.padding, @num = n.[next] FROM numbers n WITH (UPDLOCK, ROWLOCK) WHERE n.company_id = @company_id AND n.[type] = @type; IF @@ROWCOUNT = 0 THROW 50000, ''Number type not found for company'', 1; SET @value = @prefix + CASE WHEN @padding > 0 THEN RIGHT(REPLICATE(''0'', @padding) + CAST(@num AS NVARCHAR(20)), @padding) ELSE CAST(@num AS NVARCHAR(20)) END; END '); `; export const INIT_COMPANYUSER = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'companyuser') BEGIN CREATE TABLE companyuser ( id INT IDENTITY(1,1) PRIMARY KEY, google_auth_id INT NOT NULL, company_id INT NOT NULL, CONSTRAINT UQ_companyuser_google_auth_company UNIQUE (google_auth_id, company_id), CONSTRAINT FK_companyuser_google_auth FOREIGN KEY (google_auth_id) REFERENCES google_auth(id) ON DELETE CASCADE, CONSTRAINT FK_companyuser_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE ); END `; export const INIT_SYNCPOS = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'syncpos') BEGIN CREATE TABLE syncpos ( id INT IDENTITY(1,1) PRIMARY KEY, company_id INT NOT NULL, paircode NVARCHAR(50) NOT NULL, token NVARCHAR(255) NULL, CONSTRAINT FK_syncpos_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE, CONSTRAINT UQ_syncpos_paircode UNIQUE (paircode), CONSTRAINT UQ_syncpos_token UNIQUE (token) ); END `; export const INIT_COMPANYSETTING = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'companysetting') BEGIN CREATE TABLE companysetting ( id INT IDENTITY(1,1) PRIMARY KEY, company_id INT NOT NULL, [key] NVARCHAR(255) NOT NULL, value NVARCHAR(MAX) NOT NULL, frontend BIT NOT NULL CONSTRAINT DF_companysetting_frontend DEFAULT 0, writeonly BIT NOT NULL CONSTRAINT DF_companysetting_writeonly DEFAULT 0, CONSTRAINT UQ_companysetting_company_key UNIQUE (company_id, [key]), CONSTRAINT FK_companysetting_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE ); END `; export const INIT_POSTERMINAL = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'posterminal') BEGIN CREATE TABLE posterminal ( id INT IDENTITY(1,1) PRIMARY KEY, company_id INT NOT NULL, name NVARCHAR(255) NOT NULL, token NVARCHAR(255) NULL, pairingpin NVARCHAR(50) NOT NULL, CONSTRAINT FK_posterminal_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE ); CREATE UNIQUE INDEX UQ_posterminal_token ON posterminal(token) WHERE token IS NOT NULL; END `; export const INIT_POSTERMINALCATEGORY = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'posterminalcategory') BEGIN CREATE TABLE posterminalcategory ( id INT IDENTITY(1,1) PRIMARY KEY, posterminal_id INT NOT NULL, category_id INT NOT NULL, CONSTRAINT FK_posterminalcategory_posterminal FOREIGN KEY (posterminal_id) REFERENCES posterminal(id) ON DELETE CASCADE, CONSTRAINT FK_posterminalcategory_category FOREIGN KEY (category_id) REFERENCES category(id) ON DELETE NO ACTION, CONSTRAINT UQ_posterminalcategory_posterminal_category UNIQUE (posterminal_id, category_id) ); END `; export const INIT_PRINTER = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'printer') BEGIN CREATE TABLE printer ( id INT IDENTITY(1,1) PRIMARY KEY, company_id INT NOT NULL, address NVARCHAR(255) NOT NULL, type NVARCHAR(50) NOT NULL, model NVARCHAR(255) NULL, serial NVARCHAR(255) NULL, sort INT NOT NULL CONSTRAINT DF_printer_sort DEFAULT 0, [desc] NVARCHAR(MAX) NULL, CONSTRAINT FK_printer_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE, CONSTRAINT CK_printer_type CHECK (type IN ('esc/pos', 'zpl', 'ipp')) ); END `; export const INIT_LABELTEMPLATE = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'labeltemplate') BEGIN CREATE TABLE labeltemplate ( id INT IDENTITY(1,1) PRIMARY KEY, company_id INT NOT NULL, name NVARCHAR(255) NOT NULL, width INT NOT NULL, height INT NOT NULL, dpi INT NOT NULL, type NVARCHAR(50) NOT NULL, content NVARCHAR(MAX) NOT NULL, thumb VARBINARY(MAX) NULL, CONSTRAINT FK_labeltemplate_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE, CONSTRAINT CK_labeltemplate_dpi CHECK (dpi IN (203, 300)), CONSTRAINT CK_labeltemplate_type CHECK (type IN ('zpl')) ); END `; export const INIT_FILE = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'file') BEGIN CREATE TABLE [file] ( id INT IDENTITY(1,1) PRIMARY KEY, hash NVARCHAR(64) NOT NULL, mimetype NVARCHAR(255) NOT NULL, name NVARCHAR(255) NOT NULL, size BIGINT NOT NULL, created DATETIME2 NOT NULL CONSTRAINT DF_file_created DEFAULT SYSUTCDATETIME(), modified DATETIME2 NOT NULL CONSTRAINT DF_file_modified DEFAULT SYSUTCDATETIME(), content VARBINARY(MAX) NOT NULL, thumbavif VARBINARY(MAX) NULL, thumbx INT NULL, thumby INT NULL, meta NVARCHAR(MAX) NULL ); END `; export const INIT_MANIFESTATTACHMENT = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'manifestattachment') BEGIN CREATE TABLE manifestattachment ( id INT IDENTITY(1,1) PRIMARY KEY, manifest_id INT NOT NULL, file_id INT NOT NULL, sort INT NOT NULL CONSTRAINT DF_manifestattachment_sort DEFAULT 0, CONSTRAINT FK_manifestattachment_manifest FOREIGN KEY (manifest_id) REFERENCES manifest(id) ON DELETE CASCADE, CONSTRAINT FK_manifestattachment_file FOREIGN KEY (file_id) REFERENCES [file](id) ON DELETE CASCADE, CONSTRAINT UQ_manifestattachment_manifest_sort UNIQUE (manifest_id, sort) ); END `; export const INIT_MANIFESTATTACHMENT_TRIGGER = ` IF OBJECT_ID(N'dbo.TR_manifestattachment_delete_file', N'TR') IS NOT NULL DROP TRIGGER dbo.TR_manifestattachment_delete_file; EXEC(' CREATE TRIGGER dbo.TR_manifestattachment_delete_file ON manifestattachment AFTER DELETE AS BEGIN SET NOCOUNT ON; DELETE f FROM [file] f INNER JOIN deleted d ON f.id = d.file_id WHERE NOT EXISTS ( SELECT 1 FROM manifestattachment ma WHERE ma.file_id = f.id ); END '); `; /** DDL scripts in dependency order for schema init. */ export const SCHEMA_SCRIPTS = [ INIT_META, INIT_LANGUAGE, INIT_TAX, INIT_CATEGORY, INIT_PIC, INIT_COMPANY, INIT_GOOGLE_AUTH, INIT_CUSTOMERGROUP, INIT_COMPANY_POS_CUSTOMERGROUP_FK, INIT_ENTITY, INIT_ENTITY_COMPANY_NUMBER_UQ, INIT_ENTITYADDRESS, INIT_ENTITYCONTACT, INIT_UNIT, INIT_UNITTEXT, INIT_OPTIONSLIST, INIT_ARTICLELIST, INIT_BRAND, INIT_ARTICLE, INIT_ARTICLEEAN, INIT_ARTICLEPIC, INIT_BRANDPIC, INIT_ARTICLELISTARTICLE, INIT_OPTIONSLISTARTICLE, INIT_CATEGORYARTICLE, INIT_CATEGORYPIC, INIT_CATEGORYTEXT, INIT_ARTICLENAME, INIT_PRICE, INIT_ARTICLE_SUPPLIER, INIT_MANIFEST, INIT_MANIFESTARTICLE, INIT_WAREHOUSE, INIT_LEDGER, INIT_NUMBERS, INIT_GET_NEXT_NUMBER_WITH_PREFIX_AND_PADDING, INIT_PEEK_NEXT_NUMBER_WITH_PREFIX_AND_PADDING, INIT_COMPANYUSER, INIT_ARTICLEHISTORY, INIT_ENTITYHISTORY, INIT_SYNCPOS, INIT_COMPANYSETTING, INIT_POSTERMINAL, INIT_POSTERMINALCATEGORY, INIT_PRINTER, INIT_LABELTEMPLATE, INIT_FILE, INIT_MANIFESTATTACHMENT, INIT_MANIFESTATTACHMENT_TRIGGER, ];