Trade Finance + Other changes
This commit is contained in:
BIN
ITSA/Data Reconciliation/Contracts and Costs.xlsx
Normal file
BIN
ITSA/Data Reconciliation/Contracts and Costs.xlsx
Normal file
Binary file not shown.
BIN
ITSA/Data Reconciliation/ITSA_Forward_Position_Comparison.xlsx
Normal file
BIN
ITSA/Data Reconciliation/ITSA_Forward_Position_Comparison.xlsx
Normal file
Binary file not shown.
@@ -0,0 +1,267 @@
|
||||
CREATE OR REPLACE VIEW public.vw_utility_physical_pricing_definition AS
|
||||
SELECT
|
||||
'Purchase'::text AS "strContractType",
|
||||
pc.line AS "intContractLineId",
|
||||
pc.id AS "intPricingComponentId",
|
||||
pc.price_index AS "intPriceCurveId",
|
||||
vpc."strPriceDescription",
|
||||
vpc."strPriceIndex",
|
||||
vpc."strCurrency",
|
||||
vpc."strUnit",
|
||||
vpc."strPriceCurveType",
|
||||
vpc."strPriceArea",
|
||||
vpc."strPriceCalendar",
|
||||
vpc."strPricingType",
|
||||
vpc."dtmPricingStartDate",
|
||||
vpc."dtmPricingEndDate",
|
||||
vpc."strPricingMonth"
|
||||
FROM pricing_component pc
|
||||
LEFT JOIN vw_utility_price_curve vpc
|
||||
ON vpc."intPriceCurveId" = pc.price_index
|
||||
WHERE pc.line IS NOT NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'Sale'::text AS "strContractType",
|
||||
pc.sale_line AS "intContractLineId",
|
||||
pc.id AS "intPricingComponentId",
|
||||
pc.price_index AS "intPriceCurveId",
|
||||
vpc."strPriceDescription",
|
||||
vpc."strPriceIndex",
|
||||
vpc."strCurrency",
|
||||
vpc."strUnit",
|
||||
vpc."strPriceCurveType",
|
||||
vpc."strPriceArea",
|
||||
vpc."strPriceCalendar",
|
||||
vpc."strPricingType",
|
||||
vpc."dtmPricingStartDate",
|
||||
vpc."dtmPricingEndDate",
|
||||
vpc."strPricingMonth"
|
||||
FROM pricing_component pc
|
||||
LEFT JOIN vw_utility_price_curve vpc
|
||||
ON vpc."intPriceCurveId" = pc.price_index
|
||||
WHERE pc.sale_line IS NOT NULL;
|
||||
|
||||
CREATE OR REPLACE VIEW public.vw_bi_itsa_physical_open_costs_v2 AS
|
||||
WITH valuation_info AS (
|
||||
SELECT
|
||||
pvl."intContractId",
|
||||
pvl."intContractLineId",
|
||||
pvl."strState",
|
||||
avg(pvl."dblPrice") AS "avgPrice",
|
||||
avg(pvl."dblMtmPrice")::numeric(18, 2) AS "avgMtmPrice"
|
||||
FROM vw_utility_physical_valuation_line pvl
|
||||
GROUP BY
|
||||
pvl."intContractId",
|
||||
pvl."intContractLineId",
|
||||
pvl."strState"
|
||||
),
|
||||
utility_open_quantity AS (
|
||||
SELECT
|
||||
CASE
|
||||
WHEN uop."strContractSide" = 'purchase'::text THEN 'Purchase'::text
|
||||
WHEN uop."strContractSide" = 'sale'::text THEN 'Sale'::text
|
||||
ELSE NULL::text
|
||||
END AS "TradeType",
|
||||
uop."intContractLineId" AS "TradeLineId",
|
||||
sum(uop."dblOpenQuantity")::numeric(18, 3) AS "OpenQuantitySigned"
|
||||
FROM vw_utility_open_position uop
|
||||
GROUP BY
|
||||
CASE
|
||||
WHEN uop."strContractSide" = 'purchase'::text THEN 'Purchase'::text
|
||||
WHEN uop."strContractSide" = 'sale'::text THEN 'Sale'::text
|
||||
ELSE NULL::text
|
||||
END,
|
||||
uop."intContractLineId"
|
||||
),
|
||||
contract_open_quantity AS (
|
||||
SELECT
|
||||
'Purchase'::text AS "TradeType",
|
||||
pc."intPurchaseLineId" AS "TradeLineId",
|
||||
abs(COALESCE(uop."OpenQuantitySigned", 0::numeric))::numeric(18, 3) AS "OpenQuantity",
|
||||
COALESCE(uop."OpenQuantitySigned", 0::numeric)::numeric(18, 3) AS "OpenQuantitySigned",
|
||||
COALESCE(vi."avgMtmPrice", 0::numeric(18, 2)) AS "AvgMtmPrice",
|
||||
pc."dblTheoriticalQuantity" AS "TheoriticalQuantitySigned"
|
||||
FROM vw_utility_purchase_physical_contract pc
|
||||
LEFT JOIN utility_open_quantity uop
|
||||
ON uop."TradeType" = 'Purchase'::text
|
||||
AND uop."TradeLineId" = pc."intPurchaseLineId"
|
||||
LEFT JOIN valuation_info vi
|
||||
ON pc."intPurchaseLineId" = vi."intContractLineId"
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'Sale'::text AS "TradeType",
|
||||
pc."intSaleLineId" AS "TradeLineId",
|
||||
abs(COALESCE(uop."OpenQuantitySigned", 0::numeric))::numeric(18, 3) AS "OpenQuantity",
|
||||
COALESCE(uop."OpenQuantitySigned", 0::numeric)::numeric(18, 3) AS "OpenQuantitySigned",
|
||||
COALESCE(vi."avgMtmPrice", 0::numeric(18, 2)) AS "AvgMtmPrice",
|
||||
pc."dblTheoreticalQuantity" * '-1'::integer::numeric AS "TheoriticalQuantitySigned"
|
||||
FROM vw_utility_sale_physical_contract pc
|
||||
LEFT JOIN utility_open_quantity uop
|
||||
ON uop."TradeType" = 'Sale'::text
|
||||
AND uop."TradeLineId" = pc."intSaleLineId"
|
||||
LEFT JOIN valuation_info vi
|
||||
ON pc."intSaleLineId" = vi."intContractLineId"
|
||||
)
|
||||
SELECT
|
||||
1 AS "Group Order",
|
||||
'Price'::text AS "Group Type",
|
||||
ctr."intPurchaseLineId" AS "intTradeLineId",
|
||||
CASE
|
||||
WHEN ctr."strPricingType"::text = 'basis'::text THEN COALESCE(prc."strPriceDescription", 'Basis Price Curve Missing'::character varying)
|
||||
ELSE 'Fixed Price'::character varying
|
||||
END AS "Line Description",
|
||||
pur."dblUnitPrice" AS "Price",
|
||||
opq."OpenQuantitySigned" AS "Open Quantity Signed",
|
||||
opq."OpenQuantitySigned" * pur."dblUnitPrice" * '-1'::integer::numeric AS "Open Cost Amount",
|
||||
1 AS "intPriceGroupId",
|
||||
ctr."dtmEstimatedBLDate" AS "Estimated BL Date",
|
||||
opq."TheoriticalQuantitySigned" AS "Contract Quantity Signed",
|
||||
'Price'::text AS "Line Group"
|
||||
FROM vw_utility_dim_physical_purchase_contract ctr
|
||||
JOIN vw_utility_purchase_physical_contract pur
|
||||
ON ctr."intPurchaseLineId" = pur."intPurchaseLineId"
|
||||
JOIN contract_open_quantity opq
|
||||
ON ctr."intPurchaseLineId" = opq."TradeLineId"
|
||||
AND opq."TradeType" = 'Purchase'::text
|
||||
LEFT JOIN vw_utility_physical_pricing_definition prc
|
||||
ON prc."strContractType" = 'Purchase'::text
|
||||
AND prc."intContractLineId" = ctr."intPurchaseLineId"
|
||||
WHERE
|
||||
ctr."strPricingType"::text IN ('priced'::text, 'basis'::text)
|
||||
AND opq."OpenQuantity" <> 0::numeric
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
2 AS "Group Order",
|
||||
'MTM'::text AS "Group Type",
|
||||
ctr."intPurchaseLineId" AS "intTradeLineId",
|
||||
mtm."strComponentCurve" AS "Line Description",
|
||||
opq."AvgMtmPrice" AS "Price",
|
||||
opq."OpenQuantitySigned" AS "Open Quantity Signed",
|
||||
opq."OpenQuantitySigned" * opq."AvgMtmPrice" AS "Open Cost Amount",
|
||||
3 AS "intPriceGroupId",
|
||||
ctr."dtmEstimatedBLDate" AS "Estimated BL Date",
|
||||
opq."TheoriticalQuantitySigned" AS "Contract Quantity Signed",
|
||||
'MTM'::text AS "Line Group"
|
||||
FROM vw_utility_physical_mtm_definition mtm
|
||||
JOIN vw_utility_dim_physical_purchase_contract ctr
|
||||
ON mtm."intContractLineId" = ctr."intPurchaseLineId"
|
||||
JOIN contract_open_quantity opq
|
||||
ON ctr."intPurchaseLineId" = opq."TradeLineId"
|
||||
AND opq."TradeType" = 'Purchase'::text
|
||||
WHERE
|
||||
mtm."strContractType" = 'Purchase'::text
|
||||
AND opq."OpenQuantity" <> 0::numeric
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
3 AS "Group Order",
|
||||
'Fee'::text AS "Group Type",
|
||||
fees."intContractLineId" AS "intTradeLineId",
|
||||
fees."strProduct" AS "Line Description",
|
||||
fees."dblPrice" AS "Price",
|
||||
opq."OpenQuantitySigned" AS "Open Quantity Signed",
|
||||
CASE
|
||||
WHEN fees."strPayOrRec"::text = 'pay'::text THEN 1
|
||||
ELSE '-1'::integer
|
||||
END::numeric * opq."OpenQuantity" * fees."dblPrice" * '-1'::integer::numeric AS "Open Cost Amount",
|
||||
2 AS "intPriceGroupId",
|
||||
ctr."dtmEstimatedBLDate" AS "Estimated BL Date",
|
||||
opq."TheoriticalQuantitySigned" AS "Contract Quantity Signed",
|
||||
fees."strProductGroup" AS "Line Group"
|
||||
FROM vw_utility_contract_fees fees
|
||||
JOIN vw_utility_dim_physical_purchase_contract ctr
|
||||
ON fees."intContractLineId" = ctr."intPurchaseLineId"
|
||||
JOIN contract_open_quantity opq
|
||||
ON ctr."intPurchaseLineId" = opq."TradeLineId"
|
||||
AND opq."TradeType" = 'Purchase'::text
|
||||
WHERE
|
||||
fees."strContractType" = 'Purchase'::text
|
||||
AND opq."OpenQuantity" <> 0::numeric
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
1 AS "Group Order",
|
||||
'Price'::text AS "Group Type",
|
||||
ctr."intSaleLineId" AS "intTradeLineId",
|
||||
CASE
|
||||
WHEN ctr."strPricingType"::text = 'basis'::text THEN COALESCE(prc."strPriceDescription", 'Basis Price Curve Missing'::character varying)
|
||||
ELSE 'Fixed Price'::character varying
|
||||
END AS "Line Description",
|
||||
sale."dblUnitPrice" AS "Price",
|
||||
opq."OpenQuantitySigned" AS "Open Quantity Signed",
|
||||
opq."OpenQuantitySigned" * sale."dblUnitPrice" * '-1'::integer::numeric AS "Open Cost Amount",
|
||||
1 AS "intPriceGroupId",
|
||||
ctr."dtmEstimatedBLDate" AS "Estimated BL Date",
|
||||
opq."TheoriticalQuantitySigned" AS "Contract Quantity Signed",
|
||||
'Price'::text AS "Line Group"
|
||||
FROM vw_utility_dim_physical_sale_contract ctr
|
||||
JOIN vw_utility_sale_physical_contract sale
|
||||
ON ctr."intSaleLineId" = sale."intSaleLineId"
|
||||
JOIN contract_open_quantity opq
|
||||
ON ctr."intSaleLineId" = opq."TradeLineId"
|
||||
AND opq."TradeType" = 'Sale'::text
|
||||
LEFT JOIN vw_utility_physical_pricing_definition prc
|
||||
ON prc."strContractType" = 'Sale'::text
|
||||
AND prc."intContractLineId" = ctr."intSaleLineId"
|
||||
WHERE
|
||||
ctr."strPricingType"::text IN ('priced'::text, 'basis'::text)
|
||||
AND opq."OpenQuantity" <> 0::numeric
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
2 AS "Group Order",
|
||||
'MTM'::text AS "Group Type",
|
||||
ctr."intSaleLineId" AS "intTradeLineId",
|
||||
mtm."strComponentCurve" AS "Line Description",
|
||||
opq."AvgMtmPrice" AS "Price",
|
||||
opq."OpenQuantitySigned" AS "Open Quantity Signed",
|
||||
opq."OpenQuantitySigned" * opq."AvgMtmPrice" AS "Open Cost Amount",
|
||||
3 AS "intPriceGroupId",
|
||||
ctr."dtmEstimatedBLDate" AS "Estimated BL Date",
|
||||
opq."TheoriticalQuantitySigned" AS "Contract Quantity Signed",
|
||||
'MTM'::text AS "Line Group"
|
||||
FROM vw_utility_physical_mtm_definition mtm
|
||||
JOIN vw_utility_dim_physical_sale_contract ctr
|
||||
ON mtm."intContractLineId" = ctr."intSaleLineId"
|
||||
JOIN contract_open_quantity opq
|
||||
ON ctr."intSaleLineId" = opq."TradeLineId"
|
||||
AND opq."TradeType" = 'Sale'::text
|
||||
WHERE
|
||||
mtm."strContractType" = 'Sale'::text
|
||||
AND opq."OpenQuantity" <> 0::numeric
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
3 AS "Group Order",
|
||||
'Fee'::text AS "Group Type",
|
||||
fees."intContractLineId" AS "intTradeLineId",
|
||||
fees."strProduct" AS "Line Description",
|
||||
fees."dblPrice" AS "Price",
|
||||
opq."OpenQuantitySigned" AS "Open Quantity Signed",
|
||||
CASE
|
||||
WHEN fees."strPayOrRec"::text = 'pay'::text THEN 1
|
||||
ELSE '-1'::integer
|
||||
END::numeric * opq."OpenQuantity" * fees."dblPrice" * '-1'::integer::numeric AS "Open Cost Amount",
|
||||
2 AS "intPriceGroupId",
|
||||
ctr."dtmEstimatedBLDate" AS "Estimated BL Date",
|
||||
opq."TheoriticalQuantitySigned" AS "Contract Quantity Signed",
|
||||
fees."strProductGroup" AS "Line Group"
|
||||
FROM vw_utility_contract_fees fees
|
||||
JOIN vw_utility_dim_physical_sale_contract ctr
|
||||
ON fees."intContractLineId" = ctr."intSaleLineId"
|
||||
JOIN contract_open_quantity opq
|
||||
ON ctr."intSaleLineId" = opq."TradeLineId"
|
||||
AND opq."TradeType" = 'Sale'::text
|
||||
WHERE
|
||||
fees."strContractType" = 'Sale'::text
|
||||
AND opq."OpenQuantity" <> 0::numeric;
|
||||
154
ITSA/Data Reconciliation/update_vw_utility_open_position.sql
Normal file
154
ITSA/Data Reconciliation/update_vw_utility_open_position.sql
Normal file
@@ -0,0 +1,154 @@
|
||||
CREATE OR REPLACE VIEW public.vw_utility_open_position AS
|
||||
WITH physical_lot_quantity AS (
|
||||
SELECT
|
||||
'purchase'::text AS side,
|
||||
l.line AS contract_line_id,
|
||||
SUM(COALESCE(qh.quantity, 0::numeric)) AS physical_quantity
|
||||
FROM lot_lot l
|
||||
LEFT JOIN lot_qt_hist qh
|
||||
ON qh.lot = l.id
|
||||
AND qh.quantity_type = l.lot_state
|
||||
WHERE
|
||||
l.lot_type::text = 'physic'::text
|
||||
AND l.line IS NOT NULL
|
||||
GROUP BY
|
||||
l.line
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'sale'::text AS side,
|
||||
l.sale_line AS contract_line_id,
|
||||
SUM(COALESCE(qh.quantity, 0::numeric)) AS physical_quantity
|
||||
FROM lot_lot l
|
||||
LEFT JOIN lot_qt_hist qh
|
||||
ON qh.lot = l.id
|
||||
AND qh.quantity_type = l.lot_state
|
||||
WHERE
|
||||
l.lot_type::text = 'physic'::text
|
||||
AND l.sale_line IS NOT NULL
|
||||
GROUP BY
|
||||
l.sale_line
|
||||
),
|
||||
matched_virtual_quantity AS (
|
||||
SELECT
|
||||
'purchase'::text AS side,
|
||||
lp.line AS contract_line_id,
|
||||
SUM(lqt.lot_quantity) AS matched_quantity
|
||||
FROM lot_qt lqt
|
||||
JOIN lot_lot lp
|
||||
ON lp.id = lqt.lot_p
|
||||
WHERE
|
||||
lp.lot_type::text = 'virtual'::text
|
||||
AND lp.line IS NOT NULL
|
||||
AND lqt.lot_s IS NOT NULL
|
||||
AND lqt.lot_quantity > 0::numeric
|
||||
GROUP BY
|
||||
lp.line
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'sale'::text AS side,
|
||||
ls.sale_line AS contract_line_id,
|
||||
SUM(lqt.lot_quantity) AS matched_quantity
|
||||
FROM lot_qt lqt
|
||||
JOIN lot_lot ls
|
||||
ON ls.id = lqt.lot_s
|
||||
WHERE
|
||||
ls.lot_type::text = 'virtual'::text
|
||||
AND ls.sale_line IS NOT NULL
|
||||
AND lqt.lot_p IS NOT NULL
|
||||
AND lqt.lot_quantity > 0::numeric
|
||||
GROUP BY
|
||||
ls.sale_line
|
||||
),
|
||||
open_virtual_lots AS (
|
||||
SELECT
|
||||
'purchase'::text AS side,
|
||||
lqt.id AS int_lot_qt_id,
|
||||
lp.id AS int_lot_id,
|
||||
pl.id AS int_contract_line_id,
|
||||
pp.party AS int_counterparty_id,
|
||||
pl.product AS int_product_id,
|
||||
uom.name AS uom,
|
||||
lqt.lot_quantity AS open_quantity,
|
||||
pl.from_del AS period_start,
|
||||
pl.to_del AS period_end,
|
||||
COALESCE(plq.physical_quantity, 0::numeric) AS physical_lot_quantity,
|
||||
pl.quantity_theorical - COALESCE(plq.physical_quantity, 0::numeric)
|
||||
- COALESCE(mvq.matched_quantity, 0::numeric)
|
||||
AS open_targeted_quantity
|
||||
FROM lot_qt lqt
|
||||
JOIN lot_lot lp
|
||||
ON lp.id = lqt.lot_p
|
||||
JOIN purchase_line pl
|
||||
ON pl.id = lp.line
|
||||
JOIN purchase_purchase pp
|
||||
ON pp.id = pl.purchase
|
||||
LEFT JOIN product_uom uom
|
||||
ON uom.id = lqt.lot_unit
|
||||
LEFT JOIN physical_lot_quantity plq
|
||||
ON plq.side = 'purchase'::text
|
||||
AND plq.contract_line_id = pl.id
|
||||
LEFT JOIN matched_virtual_quantity mvq
|
||||
ON mvq.side = 'purchase'::text
|
||||
AND mvq.contract_line_id = pl.id
|
||||
WHERE
|
||||
lp.lot_type::text = 'virtual'::text
|
||||
AND lqt.lot_s IS NULL
|
||||
AND COALESCE(pl.finished, false) = false
|
||||
AND lqt.lot_quantity > 0::numeric
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'sale'::text AS side,
|
||||
lqt.id AS int_lot_qt_id,
|
||||
ls.id AS int_lot_id,
|
||||
sl.id AS int_contract_line_id,
|
||||
ss.party AS int_counterparty_id,
|
||||
sl.product AS int_product_id,
|
||||
uom.name AS uom,
|
||||
-lqt.lot_quantity AS open_quantity,
|
||||
sl.from_del AS period_start,
|
||||
sl.to_del AS period_end,
|
||||
COALESCE(plq.physical_quantity, 0::numeric) AS physical_lot_quantity,
|
||||
sl.quantity_theorical - COALESCE(plq.physical_quantity, 0::numeric)
|
||||
- COALESCE(mvq.matched_quantity, 0::numeric)
|
||||
AS open_targeted_quantity
|
||||
FROM lot_qt lqt
|
||||
JOIN lot_lot ls
|
||||
ON ls.id = lqt.lot_s
|
||||
JOIN sale_line sl
|
||||
ON sl.id = ls.sale_line
|
||||
JOIN sale_sale ss
|
||||
ON ss.id = sl.sale
|
||||
LEFT JOIN product_uom uom
|
||||
ON uom.id = lqt.lot_unit
|
||||
LEFT JOIN physical_lot_quantity plq
|
||||
ON plq.side = 'sale'::text
|
||||
AND plq.contract_line_id = sl.id
|
||||
LEFT JOIN matched_virtual_quantity mvq
|
||||
ON mvq.side = 'sale'::text
|
||||
AND mvq.contract_line_id = sl.id
|
||||
WHERE
|
||||
ls.lot_type::text = 'virtual'::text
|
||||
AND lqt.lot_p IS NULL
|
||||
AND COALESCE(sl.finished, false) = false
|
||||
AND lqt.lot_quantity > 0::numeric
|
||||
)
|
||||
SELECT
|
||||
side AS "strContractSide",
|
||||
int_lot_qt_id AS "intLotQtId",
|
||||
int_lot_id AS "intLotId",
|
||||
int_contract_line_id AS "intContractLineId",
|
||||
int_counterparty_id AS "intCounterpartyId",
|
||||
int_product_id AS "intProductId",
|
||||
uom AS "strUom",
|
||||
open_quantity AS "dblOpenQuantity",
|
||||
period_start AS "dtmPeriodStart",
|
||||
period_end AS "dtmPeriodEnd",
|
||||
physical_lot_quantity AS "dblPhysicalLotQuantity",
|
||||
open_targeted_quantity AS "dblOpenTargetedQuantity"
|
||||
FROM open_virtual_lots;
|
||||
@@ -0,0 +1,495 @@
|
||||
CREATE OR REPLACE VIEW public.vw_utility_shipment_fee_allocation AS
|
||||
WITH physical_shipment_lots AS (
|
||||
SELECT
|
||||
l.lot_shipment_in AS shipment_id,
|
||||
l.id AS lot_id,
|
||||
l.lot_type,
|
||||
l.line AS purchase_line_id,
|
||||
l.sale_line AS sale_line_id,
|
||||
qh.quantity AS shipment_quantity,
|
||||
l.lot_unit_line AS shipment_unit_id
|
||||
FROM lot_lot l
|
||||
LEFT JOIN lot_qt_hist qh
|
||||
ON qh.lot = l.id
|
||||
AND qh.quantity_type = l.lot_state
|
||||
WHERE
|
||||
l.lot_type::text = 'physic'::text
|
||||
AND l.lot_shipment_in IS NOT NULL
|
||||
),
|
||||
virtual_shipment_lots AS (
|
||||
SELECT
|
||||
q.lot_shipment_in AS shipment_id,
|
||||
vp.id AS lot_id,
|
||||
vp.lot_type,
|
||||
vp.line AS purchase_line_id,
|
||||
COALESCE(vp.sale_line, ps.sale_line) AS sale_line_id,
|
||||
q.lot_quantity AS shipment_quantity,
|
||||
q.lot_unit AS shipment_unit_id
|
||||
FROM lot_qt q
|
||||
JOIN lot_lot vp
|
||||
ON vp.id = q.lot_p
|
||||
AND vp.lot_type::text = 'virtual'::text
|
||||
LEFT JOIN lot_lot ps
|
||||
ON ps.id = q.lot_s
|
||||
WHERE
|
||||
q.lot_shipment_in IS NOT NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM physical_shipment_lots pl
|
||||
WHERE
|
||||
pl.shipment_id = q.lot_shipment_in
|
||||
AND pl.purchase_line_id = vp.line
|
||||
AND (
|
||||
pl.sale_line_id = COALESCE(vp.sale_line, ps.sale_line)
|
||||
OR COALESCE(vp.sale_line, ps.sale_line) IS NULL
|
||||
)
|
||||
)
|
||||
),
|
||||
shipment_lots AS (
|
||||
SELECT
|
||||
physical_shipment_lots.shipment_id,
|
||||
physical_shipment_lots.lot_id,
|
||||
physical_shipment_lots.lot_type,
|
||||
physical_shipment_lots.purchase_line_id,
|
||||
physical_shipment_lots.sale_line_id,
|
||||
physical_shipment_lots.shipment_quantity,
|
||||
physical_shipment_lots.shipment_unit_id
|
||||
FROM physical_shipment_lots
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
virtual_shipment_lots.shipment_id,
|
||||
virtual_shipment_lots.lot_id,
|
||||
virtual_shipment_lots.lot_type,
|
||||
virtual_shipment_lots.purchase_line_id,
|
||||
virtual_shipment_lots.sale_line_id,
|
||||
virtual_shipment_lots.shipment_quantity,
|
||||
virtual_shipment_lots.shipment_unit_id
|
||||
FROM virtual_shipment_lots
|
||||
),
|
||||
shipment_context AS (
|
||||
SELECT
|
||||
shipment_lots.shipment_id,
|
||||
shipment_lots.lot_id,
|
||||
shipment_lots.purchase_line_id,
|
||||
shipment_lots.sale_line_id,
|
||||
shipment_lots.shipment_unit_id,
|
||||
sum(COALESCE(shipment_lots.shipment_quantity, 0::numeric)) AS shipment_quantity
|
||||
FROM shipment_lots
|
||||
WHERE shipment_lots.shipment_id IS NOT NULL
|
||||
GROUP BY
|
||||
shipment_lots.shipment_id,
|
||||
shipment_lots.lot_id,
|
||||
shipment_lots.purchase_line_id,
|
||||
shipment_lots.sale_line_id,
|
||||
shipment_lots.shipment_unit_id
|
||||
),
|
||||
shipment_base AS (
|
||||
SELECT
|
||||
shipment_context.shipment_id,
|
||||
sum(shipment_context.shipment_quantity) AS shipment_quantity
|
||||
FROM shipment_context
|
||||
GROUP BY shipment_context.shipment_id
|
||||
),
|
||||
shipment_fee_candidates AS (
|
||||
SELECT
|
||||
f.id AS fee_id,
|
||||
'Shipment'::text AS fee_source,
|
||||
f.type AS fee_type,
|
||||
CASE
|
||||
WHEN f.type::text = 'ordered'::text THEN 1
|
||||
WHEN f.type::text = 'scheduled'::text THEN 2
|
||||
ELSE NULL::integer
|
||||
END AS priority,
|
||||
sb.shipment_id,
|
||||
f.product AS product_id,
|
||||
f.supplier AS supplier_id,
|
||||
f.mode AS packaging,
|
||||
f.p_r AS pay_or_rec,
|
||||
f.state,
|
||||
f.weight_type,
|
||||
COALESCE(f.quantity, 0::numeric) AS requested_quantity,
|
||||
sb.shipment_quantity,
|
||||
f.price AS fee_price,
|
||||
f.currency AS currency_id,
|
||||
f.unit AS unit_id,
|
||||
CASE
|
||||
WHEN upper(f.p_r::text) = 'REC'::text THEN 1
|
||||
ELSE '-1'::integer
|
||||
END AS sign_multiplier,
|
||||
NULL::text AS forced_cost_group
|
||||
FROM shipment_base sb
|
||||
JOIN fee_fee f
|
||||
ON f.shipment_in = sb.shipment_id
|
||||
WHERE f.type::text = ANY (ARRAY['ordered'::character varying::text, 'scheduled'::character varying::text])
|
||||
),
|
||||
shipment_fee_limits AS (
|
||||
SELECT
|
||||
c.*,
|
||||
LEAST(
|
||||
GREATEST(c.requested_quantity, 0::numeric),
|
||||
GREATEST(
|
||||
c.shipment_quantity
|
||||
- COALESCE(
|
||||
sum(c.requested_quantity) OVER (
|
||||
PARTITION BY c.shipment_id, c.product_id, c.supplier_id
|
||||
ORDER BY c.priority, c.fee_id
|
||||
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
|
||||
),
|
||||
0::numeric
|
||||
),
|
||||
0::numeric
|
||||
)
|
||||
) AS fee_allocatable_quantity
|
||||
FROM shipment_fee_candidates c
|
||||
),
|
||||
shipment_fee_allocated AS (
|
||||
SELECT
|
||||
fl.fee_id,
|
||||
fl.fee_source,
|
||||
fl.fee_type,
|
||||
fl.priority,
|
||||
fl.shipment_id,
|
||||
sc.lot_id,
|
||||
sc.purchase_line_id,
|
||||
sc.sale_line_id,
|
||||
fl.product_id,
|
||||
fl.supplier_id,
|
||||
fl.packaging,
|
||||
fl.pay_or_rec,
|
||||
fl.state,
|
||||
fl.weight_type,
|
||||
fl.requested_quantity,
|
||||
fl.shipment_quantity,
|
||||
fl.fee_price,
|
||||
fl.currency_id,
|
||||
COALESCE(fl.unit_id, sc.shipment_unit_id) AS unit_id,
|
||||
fl.sign_multiplier,
|
||||
fl.forced_cost_group,
|
||||
LEAST(
|
||||
sc.shipment_quantity,
|
||||
GREATEST(
|
||||
fl.fee_allocatable_quantity
|
||||
- COALESCE(
|
||||
sum(sc.shipment_quantity) OVER (
|
||||
PARTITION BY fl.fee_id
|
||||
ORDER BY sc.lot_id, sc.purchase_line_id, sc.sale_line_id
|
||||
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
|
||||
),
|
||||
0::numeric
|
||||
),
|
||||
0::numeric
|
||||
)
|
||||
) AS allocated_quantity
|
||||
FROM shipment_fee_limits fl
|
||||
JOIN shipment_context sc
|
||||
ON sc.shipment_id = fl.shipment_id
|
||||
),
|
||||
shipment_fee_coverage AS (
|
||||
SELECT
|
||||
shipment_fee_allocated.shipment_id,
|
||||
shipment_fee_allocated.product_id,
|
||||
shipment_fee_allocated.supplier_id,
|
||||
max(shipment_fee_allocated.shipment_quantity) AS shipment_quantity,
|
||||
sum(shipment_fee_allocated.allocated_quantity) AS allocated_quantity
|
||||
FROM shipment_fee_allocated
|
||||
GROUP BY
|
||||
shipment_fee_allocated.shipment_id,
|
||||
shipment_fee_allocated.product_id,
|
||||
shipment_fee_allocated.supplier_id
|
||||
),
|
||||
contract_budgeted_fee_candidates AS (
|
||||
SELECT
|
||||
f.id AS fee_id,
|
||||
'Purchase Contract'::text AS fee_source,
|
||||
f.type AS fee_type,
|
||||
3 AS priority,
|
||||
sc.shipment_id,
|
||||
sc.lot_id,
|
||||
sc.purchase_line_id,
|
||||
sc.sale_line_id,
|
||||
f.product AS product_id,
|
||||
f.supplier AS supplier_id,
|
||||
f.mode AS packaging,
|
||||
f.p_r AS pay_or_rec,
|
||||
f.state,
|
||||
f.weight_type,
|
||||
sc.shipment_quantity,
|
||||
f.price AS fee_price,
|
||||
f.currency AS currency_id,
|
||||
COALESCE(f.unit, sc.shipment_unit_id) AS unit_id,
|
||||
CASE
|
||||
WHEN upper(f.p_r::text) = 'REC'::text THEN '-1'::integer
|
||||
ELSE 1
|
||||
END AS sign_multiplier,
|
||||
NULL::text AS forced_cost_group
|
||||
FROM shipment_context sc
|
||||
JOIN fee_fee f
|
||||
ON f.line = sc.purchase_line_id
|
||||
WHERE
|
||||
f.type::text = 'budgeted'::text
|
||||
AND sc.purchase_line_id IS NOT NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
f.id AS fee_id,
|
||||
'Sale Contract'::text AS fee_source,
|
||||
f.type AS fee_type,
|
||||
3 AS priority,
|
||||
sc.shipment_id,
|
||||
sc.lot_id,
|
||||
sc.purchase_line_id,
|
||||
sc.sale_line_id,
|
||||
f.product AS product_id,
|
||||
f.supplier AS supplier_id,
|
||||
f.mode AS packaging,
|
||||
f.p_r AS pay_or_rec,
|
||||
f.state,
|
||||
f.weight_type,
|
||||
sc.shipment_quantity,
|
||||
f.price AS fee_price,
|
||||
f.currency AS currency_id,
|
||||
COALESCE(f.unit, sc.shipment_unit_id) AS unit_id,
|
||||
CASE
|
||||
WHEN upper(f.p_r::text) = 'REC'::text THEN 1
|
||||
ELSE '-1'::integer
|
||||
END AS sign_multiplier,
|
||||
NULL::text AS forced_cost_group
|
||||
FROM shipment_context sc
|
||||
JOIN fee_fee f
|
||||
ON f.sale_line = sc.sale_line_id
|
||||
WHERE
|
||||
f.type::text = 'budgeted'::text
|
||||
AND sc.sale_line_id IS NOT NULL
|
||||
),
|
||||
contract_budgeted_allocated AS (
|
||||
SELECT
|
||||
c.fee_id,
|
||||
c.fee_source,
|
||||
c.fee_type,
|
||||
c.priority,
|
||||
c.shipment_id,
|
||||
c.lot_id,
|
||||
c.purchase_line_id,
|
||||
c.sale_line_id,
|
||||
c.product_id,
|
||||
c.supplier_id,
|
||||
c.packaging,
|
||||
c.pay_or_rec,
|
||||
c.state,
|
||||
c.weight_type,
|
||||
c.shipment_quantity,
|
||||
c.fee_price,
|
||||
c.currency_id,
|
||||
c.unit_id,
|
||||
c.sign_multiplier,
|
||||
c.forced_cost_group,
|
||||
LEAST(
|
||||
c.shipment_quantity,
|
||||
GREATEST(
|
||||
sum(c.shipment_quantity) OVER (
|
||||
PARTITION BY c.shipment_id, c.product_id, c.supplier_id, c.fee_id
|
||||
)
|
||||
- COALESCE(fc.allocated_quantity, 0::numeric)
|
||||
- COALESCE(
|
||||
sum(c.shipment_quantity) OVER (
|
||||
PARTITION BY c.shipment_id, c.product_id, c.supplier_id, c.fee_id
|
||||
ORDER BY c.lot_id
|
||||
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
|
||||
),
|
||||
0::numeric
|
||||
),
|
||||
0::numeric
|
||||
)
|
||||
) AS allocated_quantity
|
||||
FROM contract_budgeted_fee_candidates c
|
||||
LEFT JOIN shipment_fee_coverage fc
|
||||
ON fc.shipment_id = c.shipment_id
|
||||
AND fc.product_id = c.product_id
|
||||
AND fc.supplier_id = c.supplier_id
|
||||
),
|
||||
price_rows AS (
|
||||
SELECT
|
||||
NULL::integer AS fee_id,
|
||||
'Purchase Price'::text AS fee_source,
|
||||
'price'::text AS fee_type,
|
||||
4 AS priority,
|
||||
sc.shipment_id,
|
||||
sc.lot_id,
|
||||
sc.purchase_line_id,
|
||||
sc.sale_line_id,
|
||||
pl.product AS product_id,
|
||||
NULL::integer AS supplier_id,
|
||||
NULL::character varying AS packaging,
|
||||
NULL::character varying AS pay_or_rec,
|
||||
NULL::character varying AS state,
|
||||
NULL::character varying AS weight_type,
|
||||
sc.shipment_quantity AS allocated_quantity,
|
||||
pl.unit_price AS fee_price,
|
||||
pl.currency_ AS currency_id,
|
||||
COALESCE(pl.unit, sc.shipment_unit_id) AS unit_id,
|
||||
1 AS sign_multiplier,
|
||||
'PurchasePrice'::text AS forced_cost_group
|
||||
FROM shipment_context sc
|
||||
JOIN purchase_line pl
|
||||
ON pl.id = sc.purchase_line_id
|
||||
WHERE sc.purchase_line_id IS NOT NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
NULL::integer AS fee_id,
|
||||
'Sale Price'::text AS fee_source,
|
||||
'price'::text AS fee_type,
|
||||
5 AS priority,
|
||||
sc.shipment_id,
|
||||
sc.lot_id,
|
||||
sc.purchase_line_id,
|
||||
sc.sale_line_id,
|
||||
sl.product AS product_id,
|
||||
NULL::integer AS supplier_id,
|
||||
NULL::character varying AS packaging,
|
||||
NULL::character varying AS pay_or_rec,
|
||||
NULL::character varying AS state,
|
||||
NULL::character varying AS weight_type,
|
||||
sc.shipment_quantity AS allocated_quantity,
|
||||
sl.unit_price AS fee_price,
|
||||
ss.currency AS currency_id,
|
||||
COALESCE(sl.unit, sc.shipment_unit_id) AS unit_id,
|
||||
'-1'::integer AS sign_multiplier,
|
||||
'SalePrice'::text AS forced_cost_group
|
||||
FROM shipment_context sc
|
||||
JOIN sale_line sl
|
||||
ON sl.id = sc.sale_line_id
|
||||
JOIN sale_sale ss
|
||||
ON ss.id = sl.sale
|
||||
WHERE sc.sale_line_id IS NOT NULL
|
||||
),
|
||||
final_fees AS (
|
||||
SELECT
|
||||
shipment_fee_allocated.fee_id,
|
||||
shipment_fee_allocated.fee_source,
|
||||
shipment_fee_allocated.fee_type,
|
||||
shipment_fee_allocated.priority,
|
||||
shipment_fee_allocated.shipment_id,
|
||||
shipment_fee_allocated.lot_id,
|
||||
shipment_fee_allocated.purchase_line_id,
|
||||
shipment_fee_allocated.sale_line_id,
|
||||
shipment_fee_allocated.product_id,
|
||||
shipment_fee_allocated.supplier_id,
|
||||
shipment_fee_allocated.packaging,
|
||||
shipment_fee_allocated.pay_or_rec,
|
||||
shipment_fee_allocated.state,
|
||||
shipment_fee_allocated.weight_type,
|
||||
shipment_fee_allocated.allocated_quantity AS fee_quantity,
|
||||
shipment_fee_allocated.fee_price,
|
||||
shipment_fee_allocated.currency_id,
|
||||
shipment_fee_allocated.unit_id,
|
||||
shipment_fee_allocated.sign_multiplier,
|
||||
shipment_fee_allocated.forced_cost_group
|
||||
FROM shipment_fee_allocated
|
||||
WHERE shipment_fee_allocated.allocated_quantity > 0::numeric
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
contract_budgeted_allocated.fee_id,
|
||||
contract_budgeted_allocated.fee_source,
|
||||
contract_budgeted_allocated.fee_type,
|
||||
contract_budgeted_allocated.priority,
|
||||
contract_budgeted_allocated.shipment_id,
|
||||
contract_budgeted_allocated.lot_id,
|
||||
contract_budgeted_allocated.purchase_line_id,
|
||||
contract_budgeted_allocated.sale_line_id,
|
||||
contract_budgeted_allocated.product_id,
|
||||
contract_budgeted_allocated.supplier_id,
|
||||
contract_budgeted_allocated.packaging,
|
||||
contract_budgeted_allocated.pay_or_rec,
|
||||
contract_budgeted_allocated.state,
|
||||
contract_budgeted_allocated.weight_type,
|
||||
contract_budgeted_allocated.allocated_quantity AS fee_quantity,
|
||||
contract_budgeted_allocated.fee_price,
|
||||
contract_budgeted_allocated.currency_id,
|
||||
contract_budgeted_allocated.unit_id,
|
||||
contract_budgeted_allocated.sign_multiplier,
|
||||
contract_budgeted_allocated.forced_cost_group
|
||||
FROM contract_budgeted_allocated
|
||||
WHERE contract_budgeted_allocated.allocated_quantity > 0::numeric
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
price_rows.fee_id,
|
||||
price_rows.fee_source,
|
||||
price_rows.fee_type,
|
||||
price_rows.priority,
|
||||
price_rows.shipment_id,
|
||||
price_rows.lot_id,
|
||||
price_rows.purchase_line_id,
|
||||
price_rows.sale_line_id,
|
||||
price_rows.product_id,
|
||||
price_rows.supplier_id,
|
||||
price_rows.packaging,
|
||||
price_rows.pay_or_rec,
|
||||
price_rows.state,
|
||||
price_rows.weight_type,
|
||||
price_rows.allocated_quantity AS fee_quantity,
|
||||
price_rows.fee_price,
|
||||
price_rows.currency_id,
|
||||
price_rows.unit_id,
|
||||
price_rows.sign_multiplier,
|
||||
price_rows.forced_cost_group
|
||||
FROM price_rows
|
||||
WHERE price_rows.allocated_quantity > 0::numeric
|
||||
)
|
||||
SELECT
|
||||
f.shipment_id AS "intShipmentId",
|
||||
f.lot_id AS "intLotId",
|
||||
COALESCE(f.fee_id, 0) AS "intFeeId",
|
||||
f.fee_source AS "strFeeSource",
|
||||
f.fee_type AS "strFeeType",
|
||||
f.priority AS "intPriority",
|
||||
COALESCE(f.purchase_line_id, 0) AS "intPurchaseLineId",
|
||||
COALESCE(f.sale_line_id, 0) AS "intSaleLineId",
|
||||
COALESCE(f.product_id, 0) AS "intProductId",
|
||||
COALESCE(p.code, ''::character varying) AS "strFee",
|
||||
COALESCE(f.supplier_id, 0) AS "intSupplierId",
|
||||
COALESCE(sup.name, ''::character varying) AS "strSupplier",
|
||||
COALESCE(f.packaging, ''::character varying) AS "strPackaging",
|
||||
COALESCE(f.pay_or_rec, ''::character varying) AS "strPayOrRec",
|
||||
COALESCE(f.state, ''::character varying) AS "strState",
|
||||
CASE
|
||||
WHEN upper(f.weight_type::text) = 'BRUT'::text THEN 'Gross'::text
|
||||
ELSE 'Net'::text
|
||||
END AS "strWeighingType",
|
||||
f.fee_quantity AS "dblQuantity",
|
||||
f.fee_price AS "dblPrice",
|
||||
COALESCE(cur.name, ''::character varying) AS "strCurrency",
|
||||
COALESCE(uom.name, 'Mt'::character varying) AS "strUnit",
|
||||
f.fee_quantity * f.fee_price AS "dblAmount",
|
||||
COALESCE(
|
||||
f.forced_cost_group,
|
||||
CASE
|
||||
WHEN upper(p.code::text) ~~ '%FREIGHT%'::text THEN 'Freight'::text
|
||||
WHEN upper(p.code::text) ~~ '%PROFIT SHARING%'::text THEN 'Profit Sharing'::text
|
||||
ELSE 'Other Costs'::text
|
||||
END
|
||||
) AS "strCostGroup",
|
||||
CASE
|
||||
WHEN f.pay_or_rec::text = 'pay'::text THEN - 1::numeric
|
||||
WHEN f.pay_or_rec::text = 'rec'::text THEN 1::numeric
|
||||
WHEN f.fee_source = 'Purchase Price'::text AND f.fee_type::text = 'price'::text THEN - 1::numeric
|
||||
WHEN f.fee_source = 'Sale Price'::text AND f.fee_type::text = 'price'::text THEN 1::numeric
|
||||
ELSE - 1::numeric
|
||||
END AS "intSignMultiplier"
|
||||
FROM final_fees f
|
||||
JOIN product_product p
|
||||
ON p.id = f.product_id
|
||||
LEFT JOIN party_party sup
|
||||
ON sup.id = f.supplier_id
|
||||
LEFT JOIN currency_currency cur
|
||||
ON cur.id = f.currency_id
|
||||
LEFT JOIN product_uom uom
|
||||
ON uom.id = f.unit_id;
|
||||
@@ -0,0 +1,382 @@
|
||||
CREATE OR REPLACE VIEW public.vw_utility_trade_pnl_quantities AS
|
||||
WITH lot_current_qty AS (
|
||||
SELECT
|
||||
l.id AS lot_id,
|
||||
round(
|
||||
COALESCE(lh.quantity, 0::numeric)
|
||||
* COALESCE(lot_uom.factor, 1::double precision)::numeric
|
||||
/ NULLIF(COALESCE(line_uom.factor, 1::double precision)::numeric, 0::numeric),
|
||||
5
|
||||
) AS pnl_quantity,
|
||||
lot_uom.name AS quantity_uom
|
||||
FROM lot_lot l
|
||||
LEFT JOIN lot_qt_hist lh ON lh.lot = l.id AND lh.quantity_type = l.lot_state
|
||||
LEFT JOIN product_uom lot_uom ON lot_uom.id = l.lot_unit_line
|
||||
LEFT JOIN purchase_line pl ON pl.id = l.line
|
||||
LEFT JOIN sale_line sl ON sl.id = l.sale_line
|
||||
LEFT JOIN product_uom line_uom ON line_uom.id = COALESCE(pl.unit, sl.unit)
|
||||
), purchase_pnl_lots AS (
|
||||
SELECT
|
||||
'purchase'::text AS pnl_side,
|
||||
p.id AS purchase_id,
|
||||
p.number AS purchase_number,
|
||||
p.reference AS purchase_reference,
|
||||
pl.id AS purchase_line_id,
|
||||
pl.finished AS purchase_line_finished,
|
||||
s.id AS sale_id,
|
||||
s.number AS sale_number,
|
||||
s.reference AS sale_reference,
|
||||
sl.id AS sale_line_id,
|
||||
sl.finished AS sale_line_finished,
|
||||
l.id AS valued_lot_id,
|
||||
l.lot_type AS valued_lot_type,
|
||||
NULL::integer AS lot_qt_id,
|
||||
lcq.pnl_quantity,
|
||||
lcq.quantity_uom AS quantity_unit,
|
||||
pl.product AS product_id,
|
||||
COALESCE(shi.from_location, sint.from_location, sho.from_location, p.from_location) AS from_location_id,
|
||||
COALESCE(shi.to_location, sint.to_location, sho.to_location, p.to_location) AS to_location_id,
|
||||
shi.id AS shipment_id,
|
||||
shi.bl_date,
|
||||
pl.from_del AS delivery_date_from,
|
||||
pl.to_del AS delivery_date_to
|
||||
FROM purchase_line pl
|
||||
JOIN purchase_purchase p ON p.id = pl.purchase
|
||||
JOIN lot_lot l ON l.line = pl.id
|
||||
JOIN lot_current_qty lcq ON lcq.lot_id = l.id
|
||||
LEFT JOIN sale_line sl ON sl.id = l.sale_line
|
||||
LEFT JOIN sale_sale s ON s.id = sl.sale
|
||||
LEFT JOIN stock_shipment_in shi ON shi.id = l.lot_shipment_in
|
||||
LEFT JOIN stock_shipment_internal sint ON sint.id = l.lot_shipment_internal
|
||||
LEFT JOIN stock_shipment_out sho ON sho.id = l.lot_shipment_out
|
||||
WHERE pl.type::text = 'line'::text
|
||||
AND (l.lot_type::text = ANY (ARRAY['virtual'::character varying::text, 'physic'::character varying::text]))
|
||||
), sale_pnl_direct_lots AS (
|
||||
SELECT
|
||||
'sale'::text AS pnl_side,
|
||||
p.id AS purchase_id,
|
||||
p.number AS purchase_number,
|
||||
p.reference AS purchase_reference,
|
||||
pl.id AS purchase_line_id,
|
||||
pl.finished AS purchase_line_finished,
|
||||
s.id AS sale_id,
|
||||
s.number AS sale_number,
|
||||
s.reference AS sale_reference,
|
||||
sl.id AS sale_line_id,
|
||||
sl.finished AS sale_line_finished,
|
||||
l.id AS valued_lot_id,
|
||||
l.lot_type AS valued_lot_type,
|
||||
NULL::integer AS lot_qt_id,
|
||||
lcq.pnl_quantity,
|
||||
lcq.quantity_uom AS quantity_unit,
|
||||
sl.product AS product_id,
|
||||
COALESCE(shi.from_location, sint.from_location, sho.from_location, s.from_location) AS from_location_id,
|
||||
COALESCE(shi.to_location, sint.to_location, sho.to_location, s.to_location) AS to_location_id,
|
||||
shi.id AS shipment_id,
|
||||
shi.bl_date,
|
||||
sl.from_del AS delivery_date_from,
|
||||
sl.to_del AS delivery_date_to
|
||||
FROM purchase_line pl
|
||||
JOIN purchase_purchase p ON p.id = pl.purchase
|
||||
JOIN lot_lot l ON l.line = pl.id AND l.sale_line IS NOT NULL
|
||||
JOIN sale_line sl ON sl.id = l.sale_line
|
||||
JOIN sale_sale s ON s.id = sl.sale
|
||||
JOIN lot_current_qty lcq ON lcq.lot_id = l.id
|
||||
LEFT JOIN stock_shipment_in shi ON shi.id = l.lot_shipment_in
|
||||
LEFT JOIN stock_shipment_internal sint ON sint.id = l.lot_shipment_internal
|
||||
LEFT JOIN stock_shipment_out sho ON sho.id = l.lot_shipment_out
|
||||
WHERE pl.type::text = 'line'::text
|
||||
AND l.lot_type::text = 'physic'::text
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM purchase_pnl_lots ppl
|
||||
WHERE ppl.valued_lot_id = l.id
|
||||
AND ppl.sale_line_id = sl.id
|
||||
)
|
||||
), sale_pnl_matched_virtual_lots AS (
|
||||
SELECT
|
||||
'sale'::text AS pnl_side,
|
||||
p.id AS purchase_id,
|
||||
p.number AS purchase_number,
|
||||
p.reference AS purchase_reference,
|
||||
pl.id AS purchase_line_id,
|
||||
pl.finished AS purchase_line_finished,
|
||||
s.id AS sale_id,
|
||||
s.number AS sale_number,
|
||||
s.reference AS sale_reference,
|
||||
sl.id AS sale_line_id,
|
||||
sl.finished AS sale_line_finished,
|
||||
sale_lot.id AS valued_lot_id,
|
||||
sale_lot.lot_type AS valued_lot_type,
|
||||
lqt.id AS lot_qt_id,
|
||||
lcq.pnl_quantity,
|
||||
lcq.quantity_uom AS quantity_unit,
|
||||
sl.product AS product_id,
|
||||
COALESCE(shi.from_location, sint.from_location, sho.from_location, s.from_location) AS from_location_id,
|
||||
COALESCE(shi.to_location, sint.to_location, sho.to_location, s.to_location) AS to_location_id,
|
||||
shi.id AS shipment_id,
|
||||
shi.bl_date,
|
||||
sl.from_del AS delivery_date_from,
|
||||
sl.to_del AS delivery_date_to
|
||||
FROM purchase_line pl
|
||||
JOIN purchase_purchase p ON p.id = pl.purchase
|
||||
JOIN lot_lot purchase_lot ON purchase_lot.line = pl.id
|
||||
AND purchase_lot.lot_type::text = 'virtual'::text
|
||||
AND purchase_lot.sale_line IS NULL
|
||||
JOIN lot_qt lqt ON lqt.lot_p = purchase_lot.id
|
||||
AND lqt.lot_s IS NOT NULL
|
||||
AND lqt.lot_quantity > 0::numeric
|
||||
JOIN lot_lot sale_lot ON sale_lot.id = lqt.lot_s
|
||||
AND sale_lot.lot_type::text = 'virtual'::text
|
||||
JOIN sale_line sl ON sl.id = sale_lot.sale_line
|
||||
JOIN sale_sale s ON s.id = sl.sale
|
||||
JOIN lot_current_qty lcq ON lcq.lot_id = sale_lot.id
|
||||
LEFT JOIN stock_shipment_in shi ON shi.id = lqt.lot_shipment_in
|
||||
LEFT JOIN stock_shipment_internal sint ON sint.id = lqt.lot_shipment_internal
|
||||
LEFT JOIN stock_shipment_out sho ON sho.id = lqt.lot_shipment_out
|
||||
WHERE pl.type::text = 'line'::text
|
||||
), sale_pnl_unmatched_lots AS (
|
||||
SELECT
|
||||
'sale'::text AS pnl_side,
|
||||
NULL::integer AS purchase_id,
|
||||
NULL::character varying AS purchase_number,
|
||||
NULL::character varying AS purchase_reference,
|
||||
NULL::integer AS purchase_line_id,
|
||||
NULL::boolean AS purchase_line_finished,
|
||||
s.id AS sale_id,
|
||||
s.number AS sale_number,
|
||||
s.reference AS sale_reference,
|
||||
sl.id AS sale_line_id,
|
||||
sl.finished AS sale_line_finished,
|
||||
l.id AS valued_lot_id,
|
||||
l.lot_type AS valued_lot_type,
|
||||
NULL::integer AS lot_qt_id,
|
||||
lcq.pnl_quantity,
|
||||
lcq.quantity_uom AS quantity_unit,
|
||||
sl.product AS product_id,
|
||||
COALESCE(shi.from_location, sint.from_location, sho.from_location, s.from_location) AS from_location_id,
|
||||
COALESCE(shi.to_location, sint.to_location, sho.to_location, s.to_location) AS to_location_id,
|
||||
shi.id AS shipment_id,
|
||||
shi.bl_date,
|
||||
sl.from_del AS delivery_date_from,
|
||||
sl.to_del AS delivery_date_to
|
||||
FROM sale_line sl
|
||||
JOIN sale_sale s ON s.id = sl.sale
|
||||
JOIN lot_lot l ON l.sale_line = sl.id
|
||||
AND l.line IS NULL
|
||||
AND (l.lot_type::text = ANY (ARRAY['virtual'::character varying::text, 'physic'::character varying::text]))
|
||||
JOIN lot_current_qty lcq ON lcq.lot_id = l.id
|
||||
LEFT JOIN stock_shipment_in shi ON shi.id = l.lot_shipment_in
|
||||
LEFT JOIN stock_shipment_internal sint ON sint.id = l.lot_shipment_internal
|
||||
LEFT JOIN stock_shipment_out sho ON sho.id = l.lot_shipment_out
|
||||
WHERE sl.type::text = 'line'::text
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM lot_qt lqt
|
||||
WHERE lqt.lot_s = l.id
|
||||
AND lqt.lot_p IS NOT NULL
|
||||
AND lqt.lot_quantity > 0::numeric
|
||||
)
|
||||
), pnl_lot_rows AS (
|
||||
SELECT
|
||||
purchase_pnl_lots.pnl_side,
|
||||
purchase_pnl_lots.purchase_id,
|
||||
purchase_pnl_lots.purchase_number,
|
||||
purchase_pnl_lots.purchase_reference,
|
||||
purchase_pnl_lots.purchase_line_id,
|
||||
purchase_pnl_lots.purchase_line_finished,
|
||||
purchase_pnl_lots.sale_id,
|
||||
purchase_pnl_lots.sale_number,
|
||||
purchase_pnl_lots.sale_reference,
|
||||
purchase_pnl_lots.sale_line_id,
|
||||
purchase_pnl_lots.sale_line_finished,
|
||||
purchase_pnl_lots.valued_lot_id,
|
||||
purchase_pnl_lots.valued_lot_type,
|
||||
purchase_pnl_lots.lot_qt_id,
|
||||
purchase_pnl_lots.pnl_quantity,
|
||||
purchase_pnl_lots.quantity_unit,
|
||||
purchase_pnl_lots.product_id,
|
||||
purchase_pnl_lots.from_location_id,
|
||||
purchase_pnl_lots.to_location_id,
|
||||
purchase_pnl_lots.shipment_id,
|
||||
purchase_pnl_lots.bl_date,
|
||||
purchase_pnl_lots.delivery_date_from,
|
||||
purchase_pnl_lots.delivery_date_to
|
||||
FROM purchase_pnl_lots
|
||||
UNION ALL
|
||||
SELECT
|
||||
sale_pnl_direct_lots.pnl_side,
|
||||
sale_pnl_direct_lots.purchase_id,
|
||||
sale_pnl_direct_lots.purchase_number,
|
||||
sale_pnl_direct_lots.purchase_reference,
|
||||
sale_pnl_direct_lots.purchase_line_id,
|
||||
sale_pnl_direct_lots.purchase_line_finished,
|
||||
sale_pnl_direct_lots.sale_id,
|
||||
sale_pnl_direct_lots.sale_number,
|
||||
sale_pnl_direct_lots.sale_reference,
|
||||
sale_pnl_direct_lots.sale_line_id,
|
||||
sale_pnl_direct_lots.sale_line_finished,
|
||||
sale_pnl_direct_lots.valued_lot_id,
|
||||
sale_pnl_direct_lots.valued_lot_type,
|
||||
sale_pnl_direct_lots.lot_qt_id,
|
||||
sale_pnl_direct_lots.pnl_quantity,
|
||||
sale_pnl_direct_lots.quantity_unit,
|
||||
sale_pnl_direct_lots.product_id,
|
||||
sale_pnl_direct_lots.from_location_id,
|
||||
sale_pnl_direct_lots.to_location_id,
|
||||
sale_pnl_direct_lots.shipment_id,
|
||||
sale_pnl_direct_lots.bl_date,
|
||||
sale_pnl_direct_lots.delivery_date_from,
|
||||
sale_pnl_direct_lots.delivery_date_to
|
||||
FROM sale_pnl_direct_lots
|
||||
UNION ALL
|
||||
SELECT
|
||||
sale_pnl_matched_virtual_lots.pnl_side,
|
||||
sale_pnl_matched_virtual_lots.purchase_id,
|
||||
sale_pnl_matched_virtual_lots.purchase_number,
|
||||
sale_pnl_matched_virtual_lots.purchase_reference,
|
||||
sale_pnl_matched_virtual_lots.purchase_line_id,
|
||||
sale_pnl_matched_virtual_lots.purchase_line_finished,
|
||||
sale_pnl_matched_virtual_lots.sale_id,
|
||||
sale_pnl_matched_virtual_lots.sale_number,
|
||||
sale_pnl_matched_virtual_lots.sale_reference,
|
||||
sale_pnl_matched_virtual_lots.sale_line_id,
|
||||
sale_pnl_matched_virtual_lots.sale_line_finished,
|
||||
sale_pnl_matched_virtual_lots.valued_lot_id,
|
||||
sale_pnl_matched_virtual_lots.valued_lot_type,
|
||||
sale_pnl_matched_virtual_lots.lot_qt_id,
|
||||
sale_pnl_matched_virtual_lots.pnl_quantity,
|
||||
sale_pnl_matched_virtual_lots.quantity_unit,
|
||||
sale_pnl_matched_virtual_lots.product_id,
|
||||
sale_pnl_matched_virtual_lots.from_location_id,
|
||||
sale_pnl_matched_virtual_lots.to_location_id,
|
||||
sale_pnl_matched_virtual_lots.shipment_id,
|
||||
sale_pnl_matched_virtual_lots.bl_date,
|
||||
sale_pnl_matched_virtual_lots.delivery_date_from,
|
||||
sale_pnl_matched_virtual_lots.delivery_date_to
|
||||
FROM sale_pnl_matched_virtual_lots
|
||||
UNION ALL
|
||||
SELECT
|
||||
sale_pnl_unmatched_lots.pnl_side,
|
||||
sale_pnl_unmatched_lots.purchase_id,
|
||||
sale_pnl_unmatched_lots.purchase_number,
|
||||
sale_pnl_unmatched_lots.purchase_reference,
|
||||
sale_pnl_unmatched_lots.purchase_line_id,
|
||||
sale_pnl_unmatched_lots.purchase_line_finished,
|
||||
sale_pnl_unmatched_lots.sale_id,
|
||||
sale_pnl_unmatched_lots.sale_number,
|
||||
sale_pnl_unmatched_lots.sale_reference,
|
||||
sale_pnl_unmatched_lots.sale_line_id,
|
||||
sale_pnl_unmatched_lots.sale_line_finished,
|
||||
sale_pnl_unmatched_lots.valued_lot_id,
|
||||
sale_pnl_unmatched_lots.valued_lot_type,
|
||||
sale_pnl_unmatched_lots.lot_qt_id,
|
||||
sale_pnl_unmatched_lots.pnl_quantity,
|
||||
sale_pnl_unmatched_lots.quantity_unit,
|
||||
sale_pnl_unmatched_lots.product_id,
|
||||
sale_pnl_unmatched_lots.from_location_id,
|
||||
sale_pnl_unmatched_lots.to_location_id,
|
||||
sale_pnl_unmatched_lots.shipment_id,
|
||||
sale_pnl_unmatched_lots.bl_date,
|
||||
sale_pnl_unmatched_lots.delivery_date_from,
|
||||
sale_pnl_unmatched_lots.delivery_date_to
|
||||
FROM sale_pnl_unmatched_lots
|
||||
), pnl_lot_rows_with_finished AS (
|
||||
SELECT
|
||||
pnl_lot_rows.pnl_side,
|
||||
pnl_lot_rows.purchase_id,
|
||||
pnl_lot_rows.purchase_number,
|
||||
pnl_lot_rows.purchase_reference,
|
||||
pnl_lot_rows.purchase_line_id,
|
||||
pnl_lot_rows.purchase_line_finished,
|
||||
pnl_lot_rows.sale_id,
|
||||
pnl_lot_rows.sale_number,
|
||||
pnl_lot_rows.sale_reference,
|
||||
pnl_lot_rows.sale_line_id,
|
||||
pnl_lot_rows.sale_line_finished,
|
||||
pnl_lot_rows.valued_lot_id,
|
||||
pnl_lot_rows.valued_lot_type,
|
||||
pnl_lot_rows.lot_qt_id,
|
||||
pnl_lot_rows.pnl_quantity,
|
||||
pnl_lot_rows.quantity_unit,
|
||||
pnl_lot_rows.product_id,
|
||||
pnl_lot_rows.from_location_id,
|
||||
pnl_lot_rows.to_location_id,
|
||||
pnl_lot_rows.shipment_id,
|
||||
pnl_lot_rows.bl_date,
|
||||
pnl_lot_rows.delivery_date_from,
|
||||
pnl_lot_rows.delivery_date_to,
|
||||
CASE
|
||||
WHEN pnl_lot_rows.pnl_side = 'purchase'::text THEN pnl_lot_rows.purchase_line_finished
|
||||
WHEN pnl_lot_rows.pnl_side = 'sale'::text THEN pnl_lot_rows.sale_line_finished
|
||||
ELSE false
|
||||
END AS valued_line_finished,
|
||||
CASE
|
||||
WHEN pnl_lot_rows.valued_lot_type::text = 'virtual'::text
|
||||
AND (
|
||||
pnl_lot_rows.pnl_side = 'purchase'::text
|
||||
AND pnl_lot_rows.purchase_line_finished IS TRUE
|
||||
OR pnl_lot_rows.pnl_side = 'sale'::text
|
||||
AND pnl_lot_rows.sale_line_finished IS TRUE
|
||||
)
|
||||
THEN true
|
||||
ELSE false
|
||||
END AS ignored_by_finished_rule
|
||||
FROM pnl_lot_rows
|
||||
)
|
||||
SELECT
|
||||
pnl_side,
|
||||
purchase_id,
|
||||
purchase_number,
|
||||
purchase_reference,
|
||||
purchase_line_id,
|
||||
purchase_line_finished,
|
||||
sale_id,
|
||||
sale_number,
|
||||
sale_reference,
|
||||
sale_line_id,
|
||||
sale_line_finished,
|
||||
valued_lot_id,
|
||||
valued_lot_type,
|
||||
valued_line_finished,
|
||||
ignored_by_finished_rule,
|
||||
sum(pnl_quantity) AS pnl_quantity_raw,
|
||||
sum(
|
||||
CASE
|
||||
WHEN ignored_by_finished_rule THEN 0::numeric
|
||||
ELSE pnl_quantity
|
||||
END
|
||||
) AS pnl_quantity_after_finished_rule,
|
||||
quantity_unit,
|
||||
product_id,
|
||||
from_location_id,
|
||||
to_location_id,
|
||||
shipment_id,
|
||||
bl_date,
|
||||
delivery_date_from,
|
||||
delivery_date_to
|
||||
FROM pnl_lot_rows_with_finished
|
||||
GROUP BY
|
||||
pnl_side,
|
||||
purchase_id,
|
||||
purchase_number,
|
||||
purchase_reference,
|
||||
purchase_line_id,
|
||||
purchase_line_finished,
|
||||
sale_id,
|
||||
sale_number,
|
||||
sale_reference,
|
||||
sale_line_id,
|
||||
sale_line_finished,
|
||||
valued_lot_type,
|
||||
valued_lot_id,
|
||||
valued_line_finished,
|
||||
ignored_by_finished_rule,
|
||||
quantity_unit,
|
||||
product_id,
|
||||
from_location_id,
|
||||
to_location_id,
|
||||
shipment_id,
|
||||
bl_date,
|
||||
delivery_date_from,
|
||||
delivery_date_to;
|
||||
@@ -0,0 +1,361 @@
|
||||
WITH physical_shipment_lots AS (
|
||||
SELECT l.lot_shipment_in AS shipment_id,
|
||||
l.id AS lot_id,
|
||||
l.lot_type,
|
||||
l.line AS purchase_line_id,
|
||||
l.sale_line AS sale_line_id,
|
||||
qh.quantity AS shipment_quantity,
|
||||
l.lot_unit_line AS shipment_unit_id
|
||||
FROM lot_lot l
|
||||
LEFT JOIN lot_qt_hist qh ON qh.lot = l.id AND qh.quantity_type = l.lot_state
|
||||
WHERE l.lot_type::text = 'physic'::text AND l.lot_shipment_in IS NOT NULL
|
||||
), virtual_shipment_lots AS (
|
||||
SELECT q.lot_shipment_in AS shipment_id,
|
||||
vp.id AS lot_id,
|
||||
vp.lot_type,
|
||||
vp.line AS purchase_line_id,
|
||||
COALESCE(vp.sale_line, ps.sale_line) AS sale_line_id,
|
||||
q.lot_quantity AS shipment_quantity,
|
||||
q.lot_unit AS shipment_unit_id
|
||||
FROM lot_qt q
|
||||
JOIN lot_lot vp ON vp.id = q.lot_p AND vp.lot_type::text = 'virtual'::text
|
||||
LEFT JOIN lot_lot ps ON ps.id = q.lot_s
|
||||
WHERE q.lot_shipment_in IS NOT NULL AND NOT (EXISTS ( SELECT 1
|
||||
FROM physical_shipment_lots pl
|
||||
WHERE pl.shipment_id = q.lot_shipment_in AND pl.purchase_line_id = vp.line AND (pl.sale_line_id = COALESCE(vp.sale_line, ps.sale_line) OR COALESCE(vp.sale_line, ps.sale_line) IS NULL)))
|
||||
), shipment_lots AS (
|
||||
SELECT physical_shipment_lots.shipment_id,
|
||||
physical_shipment_lots.lot_id,
|
||||
physical_shipment_lots.lot_type,
|
||||
physical_shipment_lots.purchase_line_id,
|
||||
physical_shipment_lots.sale_line_id,
|
||||
physical_shipment_lots.shipment_quantity,
|
||||
physical_shipment_lots.shipment_unit_id
|
||||
FROM physical_shipment_lots
|
||||
UNION ALL
|
||||
SELECT virtual_shipment_lots.shipment_id,
|
||||
virtual_shipment_lots.lot_id,
|
||||
virtual_shipment_lots.lot_type,
|
||||
virtual_shipment_lots.purchase_line_id,
|
||||
virtual_shipment_lots.sale_line_id,
|
||||
virtual_shipment_lots.shipment_quantity,
|
||||
virtual_shipment_lots.shipment_unit_id
|
||||
FROM virtual_shipment_lots
|
||||
), shipment_context AS (
|
||||
SELECT shipment_lots.shipment_id,
|
||||
shipment_lots.lot_id,
|
||||
shipment_lots.purchase_line_id,
|
||||
shipment_lots.sale_line_id,
|
||||
shipment_lots.shipment_unit_id,
|
||||
sum(COALESCE(shipment_lots.shipment_quantity, 0::numeric)) AS shipment_quantity
|
||||
FROM shipment_lots
|
||||
WHERE shipment_lots.shipment_id IS NOT NULL
|
||||
GROUP BY shipment_lots.shipment_id, shipment_lots.lot_id, shipment_lots.purchase_line_id, shipment_lots.sale_line_id, shipment_lots.shipment_unit_id
|
||||
), shipment_base AS (
|
||||
SELECT shipment_context.shipment_id,
|
||||
sum(shipment_context.shipment_quantity) AS shipment_quantity
|
||||
FROM shipment_context
|
||||
GROUP BY shipment_context.shipment_id
|
||||
), shipment_fee_candidates AS (
|
||||
SELECT f_1.id AS fee_id,
|
||||
'Shipment'::text AS fee_source,
|
||||
f_1.type AS fee_type,
|
||||
CASE
|
||||
WHEN f_1.type::text = 'ordered'::text THEN 1
|
||||
WHEN f_1.type::text = 'scheduled'::text THEN 2
|
||||
ELSE NULL::integer
|
||||
END AS priority,
|
||||
sb.shipment_id,
|
||||
NULL::integer AS lot_id,
|
||||
NULL::integer AS purchase_line_id,
|
||||
NULL::integer AS sale_line_id,
|
||||
f_1.product AS product_id,
|
||||
f_1.supplier AS supplier_id,
|
||||
f_1.mode AS packaging,
|
||||
f_1.p_r AS pay_or_rec,
|
||||
f_1.state,
|
||||
f_1.weight_type,
|
||||
COALESCE(f_1.quantity, 0::numeric) AS requested_quantity,
|
||||
sb.shipment_quantity,
|
||||
f_1.price AS fee_price,
|
||||
f_1.currency AS currency_id,
|
||||
f_1.unit AS unit_id,
|
||||
CASE
|
||||
WHEN upper(f_1.p_r::text) = 'REC'::text THEN 1
|
||||
ELSE '-1'::integer
|
||||
END AS sign_multiplier,
|
||||
NULL::text AS forced_cost_group
|
||||
FROM shipment_base sb
|
||||
JOIN fee_fee f_1 ON f_1.shipment_in = sb.shipment_id
|
||||
WHERE f_1.type::text = ANY (ARRAY['ordered'::character varying::text, 'scheduled'::character varying::text])
|
||||
), shipment_fee_allocated AS (
|
||||
SELECT c.fee_id,
|
||||
c.fee_source,
|
||||
c.fee_type,
|
||||
c.priority,
|
||||
c.shipment_id,
|
||||
c.lot_id,
|
||||
c.purchase_line_id,
|
||||
c.sale_line_id,
|
||||
c.product_id,
|
||||
c.supplier_id,
|
||||
c.packaging,
|
||||
c.pay_or_rec,
|
||||
c.state,
|
||||
c.weight_type,
|
||||
c.requested_quantity,
|
||||
c.shipment_quantity,
|
||||
c.fee_price,
|
||||
c.currency_id,
|
||||
c.unit_id,
|
||||
c.sign_multiplier,
|
||||
c.forced_cost_group,
|
||||
LEAST(GREATEST(c.requested_quantity, 0::numeric), GREATEST(c.shipment_quantity - COALESCE(sum(c.requested_quantity) OVER (PARTITION BY c.shipment_id, c.product_id, c.supplier_id ORDER BY c.priority, c.fee_id ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0::numeric), 0::numeric)) AS allocated_quantity
|
||||
FROM shipment_fee_candidates c
|
||||
), shipment_fee_coverage AS (
|
||||
SELECT shipment_fee_allocated.shipment_id,
|
||||
shipment_fee_allocated.product_id,
|
||||
shipment_fee_allocated.supplier_id,
|
||||
max(shipment_fee_allocated.shipment_quantity) AS shipment_quantity,
|
||||
sum(shipment_fee_allocated.allocated_quantity) AS allocated_quantity
|
||||
FROM shipment_fee_allocated
|
||||
GROUP BY shipment_fee_allocated.shipment_id, shipment_fee_allocated.product_id, shipment_fee_allocated.supplier_id
|
||||
), contract_budgeted_fee_candidates AS (
|
||||
SELECT f_1.id AS fee_id,
|
||||
'Purchase Contract'::text AS fee_source,
|
||||
f_1.type AS fee_type,
|
||||
3 AS priority,
|
||||
sc.shipment_id,
|
||||
sc.lot_id,
|
||||
sc.purchase_line_id,
|
||||
sc.sale_line_id,
|
||||
f_1.product AS product_id,
|
||||
f_1.supplier AS supplier_id,
|
||||
f_1.mode AS packaging,
|
||||
f_1.p_r AS pay_or_rec,
|
||||
f_1.state,
|
||||
f_1.weight_type,
|
||||
sc.shipment_quantity,
|
||||
f_1.price AS fee_price,
|
||||
f_1.currency AS currency_id,
|
||||
COALESCE(f_1.unit, sc.shipment_unit_id) AS unit_id,
|
||||
CASE
|
||||
WHEN upper(f_1.p_r::text) = 'REC'::text THEN '-1'::integer
|
||||
ELSE 1
|
||||
END AS sign_multiplier,
|
||||
NULL::text AS forced_cost_group
|
||||
FROM shipment_context sc
|
||||
JOIN fee_fee f_1 ON f_1.line = sc.purchase_line_id
|
||||
WHERE f_1.type::text = 'budgeted'::text AND sc.purchase_line_id IS NOT NULL
|
||||
UNION ALL
|
||||
SELECT f_1.id AS fee_id,
|
||||
'Sale Contract'::text AS fee_source,
|
||||
f_1.type AS fee_type,
|
||||
3 AS priority,
|
||||
sc.shipment_id,
|
||||
sc.lot_id,
|
||||
sc.purchase_line_id,
|
||||
sc.sale_line_id,
|
||||
f_1.product AS product_id,
|
||||
f_1.supplier AS supplier_id,
|
||||
f_1.mode AS packaging,
|
||||
f_1.p_r AS pay_or_rec,
|
||||
f_1.state,
|
||||
f_1.weight_type,
|
||||
sc.shipment_quantity,
|
||||
f_1.price AS fee_price,
|
||||
f_1.currency AS currency_id,
|
||||
COALESCE(f_1.unit, sc.shipment_unit_id) AS unit_id,
|
||||
CASE
|
||||
WHEN upper(f_1.p_r::text) = 'REC'::text THEN 1
|
||||
ELSE '-1'::integer
|
||||
END AS sign_multiplier,
|
||||
NULL::text AS forced_cost_group
|
||||
FROM shipment_context sc
|
||||
JOIN fee_fee f_1 ON f_1.sale_line = sc.sale_line_id
|
||||
WHERE f_1.type::text = 'budgeted'::text AND sc.sale_line_id IS NOT NULL
|
||||
), contract_budgeted_allocated AS (
|
||||
SELECT c.fee_id,
|
||||
c.fee_source,
|
||||
c.fee_type,
|
||||
c.priority,
|
||||
c.shipment_id,
|
||||
c.lot_id,
|
||||
c.purchase_line_id,
|
||||
c.sale_line_id,
|
||||
c.product_id,
|
||||
c.supplier_id,
|
||||
c.packaging,
|
||||
c.pay_or_rec,
|
||||
c.state,
|
||||
c.weight_type,
|
||||
c.shipment_quantity,
|
||||
c.fee_price,
|
||||
c.currency_id,
|
||||
c.unit_id,
|
||||
c.sign_multiplier,
|
||||
c.forced_cost_group,
|
||||
LEAST(c.shipment_quantity, GREATEST(sum(c.shipment_quantity) OVER (PARTITION BY c.shipment_id, c.product_id, c.supplier_id, c.fee_id) - COALESCE(fc.allocated_quantity, 0::numeric) - COALESCE(sum(c.shipment_quantity) OVER (PARTITION BY c.shipment_id, c.product_id, c.supplier_id, c.fee_id ORDER BY c.lot_id ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0::numeric), 0::numeric)) AS allocated_quantity
|
||||
FROM contract_budgeted_fee_candidates c
|
||||
LEFT JOIN shipment_fee_coverage fc ON fc.shipment_id = c.shipment_id AND fc.product_id = c.product_id AND fc.supplier_id = c.supplier_id
|
||||
), price_rows AS (
|
||||
SELECT NULL::integer AS fee_id,
|
||||
'Purchase Price'::text AS fee_source,
|
||||
'price'::text AS fee_type,
|
||||
4 AS priority,
|
||||
sc.shipment_id,
|
||||
sc.lot_id,
|
||||
sc.purchase_line_id,
|
||||
sc.sale_line_id,
|
||||
pl.product AS product_id,
|
||||
NULL::integer AS supplier_id,
|
||||
NULL::character varying AS packaging,
|
||||
NULL::character varying AS pay_or_rec,
|
||||
NULL::character varying AS state,
|
||||
NULL::character varying AS weight_type,
|
||||
sc.shipment_quantity AS allocated_quantity,
|
||||
pl.unit_price AS fee_price,
|
||||
pl.currency_ AS currency_id,
|
||||
COALESCE(pl.unit, sc.shipment_unit_id) AS unit_id,
|
||||
1 AS sign_multiplier,
|
||||
'PurchasePrice'::text AS forced_cost_group
|
||||
FROM shipment_context sc
|
||||
JOIN purchase_line pl ON pl.id = sc.purchase_line_id
|
||||
WHERE sc.purchase_line_id IS NOT NULL
|
||||
UNION ALL
|
||||
SELECT NULL::integer AS fee_id,
|
||||
'Sale Price'::text AS fee_source,
|
||||
'price'::text AS fee_type,
|
||||
5 AS priority,
|
||||
sc.shipment_id,
|
||||
sc.lot_id,
|
||||
sc.purchase_line_id,
|
||||
sc.sale_line_id,
|
||||
sl.product AS product_id,
|
||||
NULL::integer AS supplier_id,
|
||||
NULL::character varying AS packaging,
|
||||
NULL::character varying AS pay_or_rec,
|
||||
NULL::character varying AS state,
|
||||
NULL::character varying AS weight_type,
|
||||
sc.shipment_quantity AS allocated_quantity,
|
||||
sl.unit_price AS fee_price,
|
||||
ss.currency AS currency_id,
|
||||
COALESCE(sl.unit, sc.shipment_unit_id) AS unit_id,
|
||||
'-1'::integer AS sign_multiplier,
|
||||
'SalePrice'::text AS forced_cost_group
|
||||
FROM shipment_context sc
|
||||
JOIN sale_line sl ON sl.id = sc.sale_line_id
|
||||
JOIN sale_sale ss ON ss.id = sl.sale
|
||||
WHERE sc.sale_line_id IS NOT NULL
|
||||
), final_fees AS (
|
||||
SELECT shipment_fee_allocated.fee_id,
|
||||
shipment_fee_allocated.fee_source,
|
||||
shipment_fee_allocated.fee_type,
|
||||
shipment_fee_allocated.priority,
|
||||
shipment_fee_allocated.shipment_id,
|
||||
shipment_fee_allocated.lot_id,
|
||||
shipment_fee_allocated.purchase_line_id,
|
||||
shipment_fee_allocated.sale_line_id,
|
||||
shipment_fee_allocated.product_id,
|
||||
shipment_fee_allocated.supplier_id,
|
||||
shipment_fee_allocated.packaging,
|
||||
shipment_fee_allocated.pay_or_rec,
|
||||
shipment_fee_allocated.state,
|
||||
shipment_fee_allocated.weight_type,
|
||||
shipment_fee_allocated.allocated_quantity AS fee_quantity,
|
||||
shipment_fee_allocated.fee_price,
|
||||
shipment_fee_allocated.currency_id,
|
||||
shipment_fee_allocated.unit_id,
|
||||
shipment_fee_allocated.sign_multiplier,
|
||||
shipment_fee_allocated.forced_cost_group
|
||||
FROM shipment_fee_allocated
|
||||
WHERE shipment_fee_allocated.allocated_quantity > 0::numeric
|
||||
UNION ALL
|
||||
SELECT contract_budgeted_allocated.fee_id,
|
||||
contract_budgeted_allocated.fee_source,
|
||||
contract_budgeted_allocated.fee_type,
|
||||
contract_budgeted_allocated.priority,
|
||||
contract_budgeted_allocated.shipment_id,
|
||||
contract_budgeted_allocated.lot_id,
|
||||
contract_budgeted_allocated.purchase_line_id,
|
||||
contract_budgeted_allocated.sale_line_id,
|
||||
contract_budgeted_allocated.product_id,
|
||||
contract_budgeted_allocated.supplier_id,
|
||||
contract_budgeted_allocated.packaging,
|
||||
contract_budgeted_allocated.pay_or_rec,
|
||||
contract_budgeted_allocated.state,
|
||||
contract_budgeted_allocated.weight_type,
|
||||
contract_budgeted_allocated.allocated_quantity AS fee_quantity,
|
||||
contract_budgeted_allocated.fee_price,
|
||||
contract_budgeted_allocated.currency_id,
|
||||
contract_budgeted_allocated.unit_id,
|
||||
contract_budgeted_allocated.sign_multiplier,
|
||||
contract_budgeted_allocated.forced_cost_group
|
||||
FROM contract_budgeted_allocated
|
||||
WHERE contract_budgeted_allocated.allocated_quantity > 0::numeric
|
||||
UNION ALL
|
||||
SELECT price_rows.fee_id,
|
||||
price_rows.fee_source,
|
||||
price_rows.fee_type,
|
||||
price_rows.priority,
|
||||
price_rows.shipment_id,
|
||||
price_rows.lot_id,
|
||||
price_rows.purchase_line_id,
|
||||
price_rows.sale_line_id,
|
||||
price_rows.product_id,
|
||||
price_rows.supplier_id,
|
||||
price_rows.packaging,
|
||||
price_rows.pay_or_rec,
|
||||
price_rows.state,
|
||||
price_rows.weight_type,
|
||||
price_rows.allocated_quantity AS fee_quantity,
|
||||
price_rows.fee_price,
|
||||
price_rows.currency_id,
|
||||
price_rows.unit_id,
|
||||
price_rows.sign_multiplier,
|
||||
price_rows.forced_cost_group
|
||||
FROM price_rows
|
||||
WHERE price_rows.allocated_quantity > 0::numeric
|
||||
)
|
||||
SELECT f.shipment_id AS "intShipmentId",
|
||||
f.lot_id AS "intLotId",
|
||||
COALESCE(f.fee_id, 0) AS "intFeeId",
|
||||
f.fee_source AS "strFeeSource",
|
||||
f.fee_type AS "strFeeType",
|
||||
f.priority AS "intPriority",
|
||||
COALESCE(f.purchase_line_id, 0) AS "intPurchaseLineId",
|
||||
COALESCE(f.sale_line_id, 0) AS "intSaleLineId",
|
||||
COALESCE(f.product_id, 0) AS "intProductId",
|
||||
COALESCE(p.code, ''::character varying) AS "strFee",
|
||||
COALESCE(f.supplier_id, 0) AS "intSupplierId",
|
||||
COALESCE(sup.name, ''::character varying) AS "strSupplier",
|
||||
COALESCE(f.packaging, ''::character varying) AS "strPackaging",
|
||||
COALESCE(f.pay_or_rec, ''::character varying) AS "strPayOrRec",
|
||||
COALESCE(f.state, ''::character varying) AS "strState",
|
||||
CASE
|
||||
WHEN upper(f.weight_type::text) = 'BRUT'::text THEN 'Gross'::text
|
||||
ELSE 'Net'::text
|
||||
END AS "strWeighingType",
|
||||
f.fee_quantity AS "dblQuantity",
|
||||
f.fee_price AS "dblPrice",
|
||||
COALESCE(cur.name, ''::character varying) AS "strCurrency",
|
||||
COALESCE(uom.name, 'Mt'::character varying) AS "strUnit",
|
||||
f.fee_quantity * f.fee_price AS "dblAmount",
|
||||
COALESCE(f.forced_cost_group,
|
||||
CASE
|
||||
WHEN upper(p.code::text) ~~ '%FREIGHT%'::text THEN 'Freight'::text
|
||||
WHEN upper(p.code::text) ~~ '%PROFIT SHARING%'::text THEN 'Profit Sharing'::text
|
||||
ELSE 'Other Costs'::text
|
||||
END) AS "strCostGroup",
|
||||
CASE
|
||||
WHEN f.pay_or_rec::text = 'pay'::text THEN - 1::numeric
|
||||
WHEN f.pay_or_rec::text = 'rec'::text THEN 1::numeric
|
||||
WHEN f.fee_source = 'Purchase Price'::text AND f.fee_type::text = 'price'::text THEN - 1::numeric
|
||||
WHEN f.fee_source = 'Sale Price'::text AND f.fee_type::text = 'price'::text THEN 1::numeric
|
||||
ELSE - 1::numeric
|
||||
END AS "intSignMultiplier"
|
||||
FROM final_fees f
|
||||
JOIN product_product p ON p.id = f.product_id
|
||||
LEFT JOIN party_party sup ON sup.id = f.supplier_id
|
||||
LEFT JOIN currency_currency cur ON cur.id = f.currency_id
|
||||
LEFT JOIN product_uom uom ON uom.id = f.unit_id;
|
||||
@@ -1,7 +1,7 @@
|
||||
# This file is part of Tradon. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.model import ModelSQL, ModelView, fields
|
||||
from trytond.model import ModelSQL, ModelView, fields, tree
|
||||
from trytond.model import sequence_ordered
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Eval, Bool
|
||||
@@ -184,7 +184,7 @@ class FacilityCovenant(ModelSQL, ModelView):
|
||||
# Facility Limit (Global Limit + Sub-Limits in one model)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class FacilityLimit(ModelSQL, ModelView):
|
||||
class FacilityLimit(tree(), ModelSQL, ModelView):
|
||||
'Facility Limit'
|
||||
__name__ = 'trade_finance.facility_limit'
|
||||
_rec_name = 'name'
|
||||
@@ -194,24 +194,41 @@ class FacilityLimit(ModelSQL, ModelView):
|
||||
states={'readonly': Bool(Eval('parent'))},
|
||||
depends=['parent'])
|
||||
parent = fields.Many2One('trade_finance.facility_limit', 'Parent Limit',
|
||||
left='left', right='right',
|
||||
ondelete='RESTRICT',
|
||||
domain=[('facility', '=', Eval('facility'))],
|
||||
depends=['facility'],
|
||||
help='Leave empty for Global Limit (root node)')
|
||||
children = fields.One2Many('trade_finance.facility_limit', 'parent',
|
||||
'Sub-Limits')
|
||||
left = fields.Integer('Left', required=True)
|
||||
right = fields.Integer('Right', required=True)
|
||||
|
||||
name = fields.Char('Name', required=True)
|
||||
alternative_name = fields.Char('Limit Alternative Name')
|
||||
financing_type = fields.Many2One('trade_finance.financing_type',
|
||||
'Financing Type', ondelete='RESTRICT')
|
||||
currency = fields.Many2One('currency.currency', 'Currency',
|
||||
ondelete='RESTRICT',
|
||||
states={'readonly': Bool(Eval('parent'))},
|
||||
depends=['parent'],
|
||||
help='Currency of the global limit. Sub-limits inherit it from '
|
||||
'their parent.')
|
||||
amount = fields.Numeric('Amount', digits=(16, 2), required=True)
|
||||
tenor = fields.Integer('Tenor (days)',
|
||||
help='Maximum duration of financing from drawdown to repayment')
|
||||
date_from = fields.Date('Valid From')
|
||||
date_to = fields.Date('Valid To')
|
||||
sequence = fields.Integer('Sequence')
|
||||
notes = fields.Text('Notes')
|
||||
|
||||
_order = [('sequence', 'ASC'), ('id', 'ASC')]
|
||||
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
super().__register__(module_name)
|
||||
cls._rebuild_tree('parent', None, 0)
|
||||
|
||||
haircuts = fields.One2Many('trade_finance.facility_limit_haircut', 'limit',
|
||||
'Haircuts')
|
||||
currencies = fields.One2Many('trade_finance.facility_limit_currency',
|
||||
@@ -230,6 +247,38 @@ class FacilityLimit(ModelSQL, ModelView):
|
||||
def default_sequence():
|
||||
return 10
|
||||
|
||||
@staticmethod
|
||||
def default_left():
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def default_right():
|
||||
return 0
|
||||
|
||||
@fields.depends('facility', 'currency', 'date_from', 'date_to', 'parent',
|
||||
'_parent_parent.facility', '_parent_parent.currency',
|
||||
'_parent_parent.date_from', '_parent_parent.date_to')
|
||||
def on_change_parent(self):
|
||||
if self.parent:
|
||||
self.facility = self.parent.facility
|
||||
self.currency = self.parent.currency
|
||||
if not self.date_from:
|
||||
self.date_from = self.parent.date_from
|
||||
if not self.date_to:
|
||||
self.date_to = self.parent.date_to
|
||||
|
||||
@fields.depends('facility', 'currency', 'date_from', 'date_to', 'parent',
|
||||
'_parent_facility.currency', '_parent_facility.date_from',
|
||||
'_parent_facility.date_to')
|
||||
def on_change_facility(self):
|
||||
if self.facility and not self.parent:
|
||||
if not self.currency:
|
||||
self.currency = self.facility.currency
|
||||
if not self.date_from:
|
||||
self.date_from = self.facility.date_from
|
||||
if not self.date_to:
|
||||
self.date_to = self.facility.date_to
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [v.copy() for v in vlist]
|
||||
@@ -237,13 +286,73 @@ class FacilityLimit(ModelSQL, ModelView):
|
||||
if values.get('parent') and not values.get('facility'):
|
||||
parent = cls(values['parent'])
|
||||
values['facility'] = parent.facility.id
|
||||
if values.get('parent'):
|
||||
parent = cls(values['parent'])
|
||||
values['currency'] = (
|
||||
parent.currency.id if parent.currency else None)
|
||||
values.setdefault('date_from', parent.date_from)
|
||||
values.setdefault('date_to', parent.date_to)
|
||||
elif values.get('facility'):
|
||||
facility = Pool().get('trade_finance.facility')(
|
||||
values['facility'])
|
||||
values.setdefault('currency',
|
||||
facility.currency.id if facility.currency else None)
|
||||
values.setdefault('date_from', facility.date_from)
|
||||
values.setdefault('date_to', facility.date_to)
|
||||
return super().create(vlist)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
args = list(args)
|
||||
actions = iter(args)
|
||||
new_args = []
|
||||
check_descendants = []
|
||||
sync_currency = []
|
||||
for limits, values in zip(actions, actions):
|
||||
values = values.copy()
|
||||
if values.get('parent'):
|
||||
parent = cls(values['parent'])
|
||||
values['facility'] = parent.facility.id
|
||||
values['currency'] = (
|
||||
parent.currency.id if parent.currency else None)
|
||||
if {'parent', 'currency'} & set(values):
|
||||
sync_currency.extend(limits)
|
||||
if {'parent', 'currency', 'date_from', 'date_to'} & set(values):
|
||||
check_descendants.extend(limits)
|
||||
new_args.extend((limits, values))
|
||||
result = super().write(*new_args)
|
||||
cls._sync_descendant_currencies(sync_currency)
|
||||
cls._check_descendants(check_descendants)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def _sync_descendant_currencies(cls, limits):
|
||||
for limit in limits:
|
||||
limit = cls(limit.id)
|
||||
if not limit.currency:
|
||||
continue
|
||||
descendants = cls.search([('parent', 'child_of', [limit.id])])
|
||||
if descendants:
|
||||
super(FacilityLimit, cls).write(
|
||||
descendants, {'currency': limit.currency.id})
|
||||
|
||||
@classmethod
|
||||
def _check_descendants(cls, limits):
|
||||
descendants = []
|
||||
for limit in limits:
|
||||
descendants.extend(
|
||||
cls.search([('parent', 'child_of', [limit.id])]))
|
||||
if descendants:
|
||||
cls.validate(descendants)
|
||||
|
||||
@classmethod
|
||||
def validate(cls, limits):
|
||||
super().validate(limits)
|
||||
for limit in limits:
|
||||
limit.check_single_root()
|
||||
limit.check_required_values()
|
||||
limit.check_parent_consistency()
|
||||
limit.check_dates_within_parent()
|
||||
limit.check_amount_vs_parent()
|
||||
limit.check_children_amounts()
|
||||
|
||||
@@ -260,6 +369,49 @@ class FacilityLimit(ModelSQL, ModelView):
|
||||
f"Limit ('{roots[0].name}'). Only one root limit is "
|
||||
f"allowed per facility.")
|
||||
|
||||
def check_required_values(self):
|
||||
if not self.currency:
|
||||
raise UserError(
|
||||
f"Limit '{self.name}' must define or inherit a currency.")
|
||||
if not self.date_from or not self.date_to:
|
||||
raise UserError(
|
||||
f"Limit '{self.name}' must define valid from and valid to "
|
||||
f"dates.")
|
||||
if self.date_from > self.date_to:
|
||||
raise UserError(
|
||||
f"Limit '{self.name}' valid from date cannot be after "
|
||||
f"its valid to date.")
|
||||
|
||||
def check_parent_consistency(self):
|
||||
if not self.parent:
|
||||
return
|
||||
if self.facility != self.parent.facility:
|
||||
raise UserError(
|
||||
f"Sub-limit '{self.name}' must belong to the same facility "
|
||||
f"as parent limit '{self.parent.name}'.")
|
||||
if self.currency != self.parent.currency:
|
||||
raise UserError(
|
||||
f"Sub-limit '{self.name}' must use the same currency as "
|
||||
f"parent limit '{self.parent.name}'.")
|
||||
|
||||
def check_dates_within_parent(self):
|
||||
if self.date_from and self.date_to and self.date_from > self.date_to:
|
||||
raise UserError(
|
||||
f"Limit '{self.name}' valid from date cannot be after its "
|
||||
f"valid to date.")
|
||||
if not self.parent:
|
||||
return
|
||||
if (self.date_from and self.parent.date_from
|
||||
and self.date_from < self.parent.date_from):
|
||||
raise UserError(
|
||||
f"Sub-limit '{self.name}' valid from date cannot be before "
|
||||
f"parent limit '{self.parent.name}' valid from date.")
|
||||
if (self.date_to and self.parent.date_to
|
||||
and self.date_to > self.parent.date_to):
|
||||
raise UserError(
|
||||
f"Sub-limit '{self.name}' valid to date cannot be after "
|
||||
f"parent limit '{self.parent.name}' valid to date.")
|
||||
|
||||
def check_amount_vs_parent(self):
|
||||
if self.parent and self.amount > self.parent.amount:
|
||||
raise UserError(
|
||||
|
||||
2
modules/trade_finance/tests/__init__.py
Normal file
2
modules/trade_finance/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file is part of Tradon. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
117
modules/trade_finance/tests/test_module.py
Normal file
117
modules/trade_finance/tests/test_module.py
Normal file
@@ -0,0 +1,117 @@
|
||||
# This file is part of Tradon. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.modules.currency.tests import create_currency
|
||||
from trytond.pool import Pool
|
||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||
|
||||
|
||||
class TradeFinanceTestCase(ModuleTestCase):
|
||||
'Test Trade Finance module'
|
||||
module = 'trade_finance'
|
||||
|
||||
def create_facility(self):
|
||||
pool = Pool()
|
||||
Bank = pool.get('bank')
|
||||
Facility = pool.get('trade_finance.facility')
|
||||
Party = pool.get('party.party')
|
||||
|
||||
currency = create_currency('USD')
|
||||
party = Party(name='Test Bank')
|
||||
party.save()
|
||||
bank = Bank(party=party)
|
||||
bank.save()
|
||||
facility = Facility(
|
||||
name='Test Facility',
|
||||
tfe=bank,
|
||||
currency=currency,
|
||||
commitment_status='committed',
|
||||
date_from=date(2026, 1, 1),
|
||||
date_to=date(2026, 12, 31))
|
||||
facility.save()
|
||||
return facility, currency
|
||||
|
||||
@with_transaction()
|
||||
def test_sublimit_inherits_currency_and_dates(self):
|
||||
'Test sublimit inherits currency and dates from parent'
|
||||
pool = Pool()
|
||||
Limit = pool.get('trade_finance.facility_limit')
|
||||
|
||||
facility, currency = self.create_facility()
|
||||
root, = Limit.create([{
|
||||
'facility': facility.id,
|
||||
'name': 'Global limit',
|
||||
'currency': currency.id,
|
||||
'amount': Decimal('100.00'),
|
||||
'date_from': date(2026, 1, 1),
|
||||
'date_to': date(2026, 12, 31),
|
||||
}])
|
||||
child, = Limit.create([{
|
||||
'parent': root.id,
|
||||
'name': 'Trading limit',
|
||||
'amount': Decimal('50.00'),
|
||||
}])
|
||||
|
||||
self.assertEqual(child.facility, facility)
|
||||
self.assertEqual(child.currency, currency)
|
||||
self.assertEqual(child.date_from, root.date_from)
|
||||
self.assertEqual(child.date_to, root.date_to)
|
||||
|
||||
@with_transaction()
|
||||
def test_sublimit_dates_must_stay_within_parent(self):
|
||||
'Test sublimit dates stay within parent dates'
|
||||
pool = Pool()
|
||||
Limit = pool.get('trade_finance.facility_limit')
|
||||
|
||||
facility, currency = self.create_facility()
|
||||
root, = Limit.create([{
|
||||
'facility': facility.id,
|
||||
'name': 'Global limit',
|
||||
'currency': currency.id,
|
||||
'amount': Decimal('100.00'),
|
||||
'date_from': date(2026, 1, 1),
|
||||
'date_to': date(2026, 12, 31),
|
||||
}])
|
||||
|
||||
with self.assertRaises(UserError):
|
||||
Limit.create([{
|
||||
'parent': root.id,
|
||||
'name': 'Late limit',
|
||||
'amount': Decimal('50.00'),
|
||||
'date_from': date(2026, 1, 1),
|
||||
'date_to': date(2027, 1, 1),
|
||||
}])
|
||||
|
||||
@with_transaction()
|
||||
def test_currency_change_cascades_to_sublimits(self):
|
||||
'Test root currency changes cascade to sublimits'
|
||||
pool = Pool()
|
||||
Limit = pool.get('trade_finance.facility_limit')
|
||||
|
||||
facility, currency = self.create_facility()
|
||||
other_currency = create_currency('EUR')
|
||||
root, = Limit.create([{
|
||||
'facility': facility.id,
|
||||
'name': 'Global limit',
|
||||
'currency': currency.id,
|
||||
'amount': Decimal('100.00'),
|
||||
'date_from': date(2026, 1, 1),
|
||||
'date_to': date(2026, 12, 31),
|
||||
}])
|
||||
child, = Limit.create([{
|
||||
'parent': root.id,
|
||||
'name': 'Trading limit',
|
||||
'amount': Decimal('50.00'),
|
||||
}])
|
||||
|
||||
Limit.write([root], {'currency': other_currency.id})
|
||||
child = Limit(child.id)
|
||||
|
||||
self.assertEqual(child.currency, other_currency)
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
@@ -30,9 +30,10 @@
|
||||
<field name="description" colspan="5"/>
|
||||
</group>
|
||||
<notebook colspan="6">
|
||||
<page string="Limits" id="limits">
|
||||
<page string="Global Limit" id="limits">
|
||||
<field name="limits" colspan="6"
|
||||
domain="[('parent', '=', None)]"/>
|
||||
domain="[('parent', '=', None)]"
|
||||
view_ids="trade_finance.facility_limit_view_tree,trade_finance.facility_limit_view_form"/>
|
||||
</page>
|
||||
<page string="Currencies" id="currencies">
|
||||
<field name="currencies" colspan="6"/>
|
||||
|
||||
@@ -6,8 +6,14 @@
|
||||
<field name="alternative_name" colspan="3"/>
|
||||
<label name="financing_type"/>
|
||||
<field name="financing_type" colspan="3"/>
|
||||
<label name="currency"/>
|
||||
<field name="currency"/>
|
||||
<label name="amount"/>
|
||||
<field name="amount"/>
|
||||
<label name="date_from"/>
|
||||
<field name="date_from"/>
|
||||
<label name="date_to"/>
|
||||
<field name="date_to"/>
|
||||
<label name="tenor"/>
|
||||
<field name="tenor"/>
|
||||
<label name="sequence"/>
|
||||
@@ -17,10 +23,13 @@
|
||||
<field name="parent" colspan="3"/>
|
||||
<label name="facility"/>
|
||||
<field name="facility" colspan="3"/>
|
||||
<label name="notes"/>
|
||||
<field name="notes" colspan="3"/>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="Sub-Limits" id="children">
|
||||
<field name="children" colspan="4"/>
|
||||
<field name="children" colspan="4"
|
||||
view_ids="trade_finance.facility_limit_view_tree,trade_finance.facility_limit_view_form"/>
|
||||
</page>
|
||||
<page string="Haircuts" id="haircuts">
|
||||
<field name="haircuts" colspan="4"/>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<tree expand="1">
|
||||
<field name="name"/>
|
||||
<field name="parent"/>
|
||||
<tree expand="1" keyword_open="1">
|
||||
<field name="name" expand="2"/>
|
||||
<field name="parent" tree_invisible="1"/>
|
||||
<field name="children" tree_invisible="1"/>
|
||||
<field name="financing_type"/>
|
||||
<field name="currency"/>
|
||||
<field name="amount"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="tenor"/>
|
||||
</tree>
|
||||
|
||||
Reference in New Issue
Block a user