--
-- This file is part of TALER
-- Copyright (C) 2025 Taler Systems SA
--
-- TALER is free software; you can redistribute it and/or modify it under the
-- terms of the GNU General Public License as published by the Free Software
-- Foundation; either version 3, or (at your option) any later version.
--
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along with
-- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>

-- @file merchant-0027.sql
-- @brief Add fractional stock support to merchant_inventory
-- @author Bohdan Potuzhnyi

BEGIN;

-- Check patch versioning is in place.
SELECT _v.register_patch('merchant-0027', NULL, NULL);

SET search_path TO merchant;

ALTER TABLE merchant_inventory
    ADD COLUMN price_array taler_amount_currency[]
        NOT NULL
        DEFAULT ARRAY[]::taler_amount_currency[],
    ADD COLUMN total_stock_frac INT4 NOT NULL DEFAULT 0,
    ADD COLUMN total_sold_frac INT4 NOT NULL DEFAULT 0,
    ADD COLUMN total_lost_frac INT4 NOT NULL DEFAULT 0,
    ADD COLUMN allow_fractional_quantity BOOL NOT NULL DEFAULT FALSE,
    ADD COLUMN fractional_precision_level INT4 NOT NULL DEFAULT 0;
COMMENT ON COLUMN merchant_inventory.price_array
    IS 'List of unit prices available for the product (multiple tiers supported).';

UPDATE merchant_inventory
   SET price_array = ARRAY[price]::taler_amount_currency[]
 WHERE price IS NOT NULL; -- theoretically all objects, but just to be sure

-- Note: price column is dropped in merchant-0028.sql

COMMENT ON COLUMN merchant_inventory.total_stock_frac
    IS 'Fractional part of stock in units of 1/1000000 of the base value';
COMMENT ON COLUMN merchant_inventory.total_sold_frac
    IS 'Fractional part of units sold in units of 1/1000000 of the base value';
COMMENT ON COLUMN merchant_inventory.total_lost_frac
    IS 'Fractional part of units lost in units of 1/1000000 of the base value';
COMMENT ON COLUMN merchant_inventory.allow_fractional_quantity
    IS 'Whether fractional stock (total_stock_frac) should be honored for this product';
COMMENT ON COLUMN merchant_inventory.fractional_precision_level
    IS 'Preset number of decimal places for fractional quantities';

ALTER TABLE merchant_inventory_locks
    ADD COLUMN total_locked_frac INT4 NOT NULL DEFAULT 0;
COMMENT ON COLUMN merchant_inventory_locks.total_locked_frac
    IS 'Fractional part of locked stock in units of 1/1000000 of the base value';

ALTER TABLE merchant_order_locks
    ADD COLUMN total_locked_frac INT4 NOT NULL DEFAULT 0;
COMMENT ON COLUMN merchant_order_locks.total_locked_frac
    IS 'Fractional part of locked stock associated with orders in units of 1/1000000 of the base value';

CREATE TABLE merchant_builtin_units
(
    unit_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    unit TEXT NOT NULL UNIQUE,
    unit_name_long TEXT NOT NULL,
    unit_name_short TEXT NOT NULL,
    unit_name_long_i18n BYTEA NOT NULL DEFAULT convert_to('{}','UTF8'),
    unit_name_short_i18n BYTEA NOT NULL DEFAULT convert_to('{}','UTF8'),
    unit_allow_fraction BOOLEAN NOT NULL DEFAULT FALSE,
    unit_precision_level INT4 NOT NULL DEFAULT 0 CHECK (unit_precision_level BETWEEN 0 AND 6),
    unit_active BOOLEAN NOT NULL DEFAULT TRUE
);
COMMENT ON TABLE merchant_builtin_units
    IS 'Global catalogue of builtin measurement units.';
COMMENT ON COLUMN merchant_builtin_units.unit_active
    IS 'Default visibility for the builtin unit; instances may override.';

CREATE TABLE merchant_custom_units
(
    unit_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    merchant_serial BIGINT NOT NULL REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE,
    unit TEXT NOT NULL,
    unit_name_long TEXT NOT NULL,
    unit_name_short TEXT NOT NULL,
    unit_name_long_i18n BYTEA NOT NULL DEFAULT convert_to('{}','UTF8'),
    unit_name_short_i18n BYTEA NOT NULL DEFAULT convert_to('{}','UTF8'),
    unit_allow_fraction BOOLEAN NOT NULL DEFAULT FALSE,
    unit_precision_level INT4 NOT NULL DEFAULT 0 CHECK (unit_precision_level BETWEEN 0 AND 6),
    unit_active BOOLEAN NOT NULL DEFAULT TRUE,
    UNIQUE (merchant_serial, unit)
);
COMMENT ON TABLE merchant_custom_units
    IS 'Per-instance custom measurement units.';

CREATE TABLE merchant_builtin_unit_overrides
(
    merchant_serial BIGINT NOT NULL REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE,
    builtin_unit_serial BIGINT NOT NULL REFERENCES merchant_builtin_units (unit_serial) ON DELETE CASCADE,
    override_allow_fraction BOOLEAN,
    override_precision_level INT4 CHECK (override_precision_level BETWEEN 0 AND 6),
    override_active BOOLEAN,
    PRIMARY KEY (merchant_serial, builtin_unit_serial)
);
COMMENT ON TABLE merchant_builtin_unit_overrides
    IS 'Per-instance overrides for builtin units (fraction policy and visibility).';

INSERT INTO merchant_builtin_units (unit, unit_name_long, unit_name_short, unit_allow_fraction, unit_precision_level, unit_active)
VALUES
    ('Piece', 'piece', 'pc', FALSE, 0, TRUE),
    ('Set', 'set', 'set', FALSE, 0, TRUE),
    ('SizeUnitCm', 'centimetre', 'cm', TRUE, 1, TRUE),
    ('SizeUnitDm', 'decimetre', 'dm', TRUE, 3, TRUE),
    ('SizeUnitFoot', 'foot', 'ft', TRUE, 3, TRUE),
    ('SizeUnitInch', 'inch', 'in', TRUE, 2, TRUE),
    ('SizeUnitM', 'metre', 'm', TRUE, 3, TRUE),
    ('SizeUnitMm', 'millimetre', 'mm', FALSE, 0, TRUE),
    ('SurfaceUnitCm2', 'square centimetre', 'cm²', TRUE, 2, TRUE),
    ('SurfaceUnitDm2', 'square decimetre', 'dm²', TRUE, 3, TRUE),
    ('SurfaceUnitFoot2', 'square foot', 'ft²', TRUE, 3, TRUE),
    ('SurfaceUnitInch2', 'square inch', 'in²', TRUE, 4, TRUE),
    ('SurfaceUnitM2', 'square metre', 'm²', TRUE, 4, TRUE),
    ('SurfaceUnitMm2', 'square millimetre', 'mm²', TRUE, 1, TRUE),
    ('TimeUnitDay', 'day', 'd', TRUE, 3, TRUE),
    ('TimeUnitHour', 'hour', 'h', TRUE, 2, TRUE),
    ('TimeUnitMinute', 'minute', 'min', TRUE, 3, TRUE),
    ('TimeUnitMonth', 'month', 'mo', TRUE, 2, TRUE),
    ('TimeUnitSecond', 'second', 's', TRUE, 3, TRUE),
    ('TimeUnitWeek', 'week', 'wk', TRUE, 3, TRUE),
    ('TimeUnitYear', 'year', 'yr', TRUE, 4, TRUE),
    ('VolumeUnitCm3', 'cubic centimetre', 'cm³', TRUE, 3, TRUE),
    ('VolumeUnitDm3', 'cubic decimetre', 'dm³', TRUE, 5, TRUE),
    ('VolumeUnitFoot3', 'cubic foot', 'ft³', TRUE, 5, TRUE),
    ('VolumeUnitGallon', 'gallon', 'gal', TRUE, 3, TRUE),
    ('VolumeUnitInch3', 'cubic inch', 'in³', TRUE, 2, TRUE),
    ('VolumeUnitLitre', 'litre', 'L', TRUE, 3, TRUE),
    ('VolumeUnitM3', 'cubic metre', 'm³', TRUE, 6, TRUE),
    ('VolumeUnitMm3', 'cubic millimetre', 'mm³', TRUE, 1, TRUE),
    ('VolumeUnitOunce', 'fluid ounce', 'fl oz', TRUE, 2, TRUE),
    ('WeightUnitG', 'gram', 'g', TRUE, 1, TRUE),
    ('WeightUnitKg', 'kilogram', 'kg', TRUE, 3, TRUE),
    ('WeightUnitMg', 'milligram', 'mg', FALSE, 0, TRUE),
    ('WeightUnitOunce', 'ounce', 'oz', TRUE, 2, TRUE),
    ('WeightUnitPound', 'pound', 'lb', TRUE, 3, TRUE),
    ('WeightUnitTon', 'metric tonne', 't', TRUE, 3, TRUE);

COMMIT;
