From a27bab4371229a1dd906e2d15fe0e90c7d2fbf09 Mon Sep 17 00:00:00 2001 From: Ryan Fleck Date: Mon, 26 Nov 2018 20:49:02 -0500 Subject: [PATCH] Add two new rules: partially complete, but running. --- .../ca_tax.context.json | 6 + org.xalgorithms.examples.ca_tax/ca_tax.rule | 34 ++++ .../provincial_vat.json | 14 ++ .../provincial_vat.table | 5 + .../proximity-gas-tax.rule | 171 ++++++++++++++++++ .../reductions_by_distance.json | 7 + .../reductions_by_distance.table | 6 + .../supplier_distances.json | 8 + .../supplier_distances.table | 6 + 9 files changed, 257 insertions(+) create mode 100644 org.xalgorithms.examples.ca_tax/ca_tax.context.json create mode 100644 org.xalgorithms.examples.ca_tax/ca_tax.rule create mode 100644 org.xalgorithms.examples.ca_tax/provincial_vat.json create mode 100644 org.xalgorithms.examples.ca_tax/provincial_vat.table create mode 100644 org.xalgorithms.examples.qc_gas_tax/proximity-gas-tax.rule create mode 100644 org.xalgorithms.examples.qc_gas_tax/reductions_by_distance.json create mode 100644 org.xalgorithms.examples.qc_gas_tax/reductions_by_distance.table create mode 100644 org.xalgorithms.examples.qc_gas_tax/supplier_distances.json create mode 100644 org.xalgorithms.examples.qc_gas_tax/supplier_distances.table diff --git a/org.xalgorithms.examples.ca_tax/ca_tax.context.json b/org.xalgorithms.examples.ca_tax/ca_tax.context.json new file mode 100644 index 0000000..d3b59d4 --- /dev/null +++ b/org.xalgorithms.examples.ca_tax/ca_tax.context.json @@ -0,0 +1,6 @@ +{ + "document" : { + "region" : "CA-ON", + "subtotal" : 10.00 + } +} \ No newline at end of file diff --git a/org.xalgorithms.examples.ca_tax/ca_tax.rule b/org.xalgorithms.examples.ca_tax/ca_tax.rule new file mode 100644 index 0000000..aeecb63 --- /dev/null +++ b/org.xalgorithms.examples.ca_tax/ca_tax.rule @@ -0,0 +1,34 @@ + +META + VERSION "0.0.3" + RUNTIME "0.4.0" + CRITICALITY "experimental" + MANAGER "Joseph Potvin " + MAINTAINER "Ryan Fleck "; + +EFFECTIVE + IN "CA-*" + FROM "2018-04-01T00:00" + TO "9999-12-30T23:59" + TIMEZONE "America/Toronto"; + +# WHEN document:transaction.currency == "CAD"; + +REQUIRE tax:provincial_vat:0.0.1 AS provincial_vat; + +ASSEMBLE applicable_vat + COLUMNS FROM table:provincial_vat + WHEN @region == document:region; + +MAP table:applicable_vat + USING HST = add(@GST, @PST); + +ASSEMBLE apply_vat + COLUMN HST FROM table:applicable_vat; + +MAP table:apply_vat + USING TaxRate = add(1, multiply(divide(1, 100), @HST)); + +MAP table:apply_vat + USING Total = multiply(@TaxRate, document:subtotal); + diff --git a/org.xalgorithms.examples.ca_tax/provincial_vat.json b/org.xalgorithms.examples.ca_tax/provincial_vat.json new file mode 100644 index 0000000..628c560 --- /dev/null +++ b/org.xalgorithms.examples.ca_tax/provincial_vat.json @@ -0,0 +1,14 @@ +[ + { "region" : "CA-NL", "GST": 5, "PST": 10}, + { "region" : "CA-PE", "GST": 5, "PST": 10}, + { "region" : "CA-NS", "GST": 5, "PST": 10}, + { "region" : "CA-NB", "GST": 5, "PST": 10}, + { "region" : "CA-QC", "GST": 5, "PST": 10}, + { "region" : "CA-ON", "GST": 5, "PST": 8}, + { "region" : "CA-MB", "GST": 5, "PST": 8}, + { "region" : "CA-SK", "GST": 5, "PST": 5}, + { "region" : "CA-AB", "GST": 5, "PST": 0}, + { "region" : "CA-BC", "GST": 5, "PST": 7}, + { "region" : "CA-YT", "GST": 5, "PST": 0}, + { "region" : "CA-NT", "GST": 5, "PST": 0} +] diff --git a/org.xalgorithms.examples.ca_tax/provincial_vat.table b/org.xalgorithms.examples.ca_tax/provincial_vat.table new file mode 100644 index 0000000..c4bbd3b --- /dev/null +++ b/org.xalgorithms.examples.ca_tax/provincial_vat.table @@ -0,0 +1,5 @@ +META + VERSION "0.0.1" + MAINTAINER "Ryan Fleck "; + +DATA provincial_vat.json 5071d3ac04ea2be8eafd8b23b3c9336a; diff --git a/org.xalgorithms.examples.qc_gas_tax/proximity-gas-tax.rule b/org.xalgorithms.examples.qc_gas_tax/proximity-gas-tax.rule new file mode 100644 index 0000000..1bf4733 --- /dev/null +++ b/org.xalgorithms.examples.qc_gas_tax/proximity-gas-tax.rule @@ -0,0 +1,171 @@ +# GOAL: This rule operates on documents that are prepared by the +# Lichen application, originating as UBL invoices. It selects invoices +# that apply to consumer petrol sales (using ISIC and UNSPSC) +# coding. For any items in the invoice that required UNSPSC code, it +# will generate a revision that adds an allowance (discount) to the +# invoice. + +EFFECTIVE + IN "CA-ON", "CA-QC" + FROM "2018-04-01T00:00" + TO "9999-12-30T23:59" + TIMEZONE "America/Toronto"; + +META + VERSION "0.0.2" + RUNTIME "0.4.0" + CRITICALITY "experimental" + MANAGER "Joseph Potvin " + MAINTAINER "Ryan Fleck "; + +# This rule only applies to invoices from suppliers in the consumer +# petrol industry (ISIC/G4711). +WHEN envelope:type == "invoice"; +WHEN envelope:parties.supplier.industry.list_id == "ISIC"; +WHEN envelope:parties.supplier.industry.value == "G4711"; + +# This rule only operates on invoice items that are coded as consumer +# petrol (UNSPSC/506505). +WHEN item:classification.list_name == "UNSPSC"; +WHEN item:classification.value == "506505"; +WHEN item:quantity.value > 0; + +# load tables that add additional information +REQUIRE org.xalgorithms.examples.qc_gas_tax:supplier_distances:0.0.1; +REQUIRE org.xalgorithms.examples.qc_gas_tax:reductions_by_distance:0.0.1; + +# PHASE ONE: Build a table that can be used to transform any items in +# the invoice that related to UNSPSC/506505. We will do this by +# loading a table of registered station distances against a table of +# discounts based on kilometer distance from the border. For this +# example, it is assumed that the author of the rule has some facility +# for generating the supplier distances table +# +# For the purposes of illustration, the supplier in the comment +# examples is "0002". +# +# Build a table that contains the seller information joined against the predefined +# reductions table. +# +# The first column selection build a basic table using the +# reductions_by_distance table. Quoting from the sample tables, this +# yields: +# +# [ +# { "distance" : 20, "reduction" : 0.00 }, +# { "distance" : 15, "reduction" : 0.02 }, +# { "distance" : 10, "reduction" : 0.04 }, +# { "distance" : 5, "reduction" : 0.06 }, +# { "distance" : 0, "reduction" : 0.08 } +# ] +# +# The second COLUMN expression performs a cartesian product against +# the table-under-construction. It also applies a condition that ONLY +# accepts a row where the supplier from the invoice matches. +# +# This yields a final table: +# +# [ +# { "distance" : 20, "reduction" : 0.00, "seller_distance" : 1.7 }, +# { "distance" : 15, "reduction" : 0.02, "seller_distance" : 1.7 }, +# { "distance" : 10, "reduction" : 0.04, "seller_distance" : 1.7 }, +# { "distance" : 5, "reduction" : 0.06, "seller_distance" : 1.7 }, +# { "distance" : 0, "reduction" : 0.08, "seller_distance" : 1.7 } +# ] +# +ASSEMBLE sellers_reductions + COLUMNS FROM table:reductions_by_distance + COLUMN seller_distance FROM table:supplier_distances WHEN "UsN8EB1QZU" == @supplier_id; + # WHEN envelope:parties.supplier.id.value == @supplier_id; + +# +# In order to determine the correct reduction, we learn WHICH bracket +# of distance that the seller matches. We do this by performing a MAP, +# FILTER, REDUCE. The first step generates the difference in distance, +# yielding: +# +# [ +# { "distance" : 20, "reduction" : 0.00, "seller_distance" : 1.7, "difference" : 18.3 }, +# { "distance" : 15, "reduction" : 0.02, "seller_distance" : 1.7, "difference" : 13.3 }, +# { "distance" : 10, "reduction" : 0.04, "seller_distance" : 1.7, "difference" : 8.3 }, +# { "distance" : 5, "reduction" : 0.06, "seller_distance" : 1.7, "difference" : 3.3 }, +# { "distance" : 0, "reduction" : 0.08, "seller_distance" : 1.7, "difference" : -1.7 } +# ] +# +MAP table:sellers_reductions + USING difference = subtract(@distance, @seller_distance); + +# Since we will be reducing using min(), we should remove any rows that are < 0 +# +# [ +# { "distance" : 20, "reduction" : 0.00, "seller_distance" : 1.7, "difference" : 18.3 }, +# { "distance" : 15, "reduction" : 0.02, "seller_distance" : 1.7, "difference" : 13.3 }, +# { "distance" : 10, "reduction" : 0.04, "seller_distance" : 1.7, "difference" : 8.3 }, +# { "distance" : 5, "reduction" : 0.06, "seller_distance" : 1.7, "difference" : 3.3 } +# ] +# +FILTER table:sellers_reductions + WHEN @difference >= 0; + +# Using the REDUCE step, we determine the minimal distance and select +# it. The accumulator we use in the reduction (min_difference) is not +# retained in the resulting table. +# +#[ +# { "distance" : 5, "reduction" : 0.06, "seller_distance" : 1.7, "difference" : 3.3 } +#] +# + +## REDUCE table:sellers_reductions +## USING min_difference = min(min_difference, @difference) +## WHEN difference == @min_difference; + +# PHASE TWO: We need to apply this final reduction ACROSS the items +# that appear in the invoice. Since this invoice originates as a UBL +# document formatted to our internal format it will have HIERARCHICAL +# columns (columns containing KEY/VALUE pair trees). Therefore the +# example will only contain the information relevant to the rule. +# +# The items table in the originating document might contain: +# +# [ +# { "classification" : { list_name: "UNSPSC", "value" : "506505" }, price: { currency_code: "CAD", "value" : "10.00" } }, +# { "classification" : { list_name: "UNSPSC", "value" : "111111" }, price: { currency_code: "CAD", "value" : "35.00" } }, +# { "classification" : { list_name: "UNSPSC", "value" : "222222" }, price: { currency_code: "CAD", "value" : "15.00" } }, +# { "classification" : { list_name: "UNSPSC", "value" : "506505" }, price: { currency_code: "CAD", "value" : "18.00" } } +# ] +# +# +# First, we use the items table as the basis for a new table. It is +# filtered according to the classification. We form additional columns +# using the cartesian product of the table we built in PHASE ONE. This yields: +# +# [ +# { "classification" : { list_name: "UNSPSC", "value" : "506505" }, "price": { currency_code: "CAD", "value" : "10.00" }, "reduction" : .06 }, +# { "classification" : { list_name: "UNSPSC", "value" : "506505" }, "price": { currency_code: "CAD", "value" : "18.00" }, "reduction" : .06 } +# ] +# +## ASSEMBLE item_reductions +## COLUMNS (id, classification, price) FROM table:items +## WHEN @classification.list_name == "UNSPSC" +## WHEN @classification.value == "506505" +## COLUMN reduction FROM table:sellers_reductions; +# +# We use MAP to add an additional NESTED COLUMN that translates to an +# allowance (a discount in this case) in UBL concepts, yielding: +# +# [ +# { "classification" : { list_name: "UNSPSC", "value" : "506505" }, "price": { currency_code: "CAD", "value" : "10.00" }, "reduction" : .06, "allowance": { "charge" : false, "amount" : { "value" : 0.6, "currency_code" : "CAD" } }, +# { "classification" : { list_name: "UNSPSC", "value" : "506505" }, "price": { currency_code: "CAD", "value" : "18.00" }, "reduction" : .06, "allowance": { "charge" : false, "amount" : { "value" : 1.08, "currency_code" : "CAD" } } +# ] +# +## MAP table:item_reductions +## USING allowance.charge = false +## USING allowance.amount.value = multiply(@price.value, @reduction) +## USING allowance.amount.currency_code = @price.currency_code; +# +# This issues a revision of the original items table, adding the +# ENTIRE allowance column. +### REVISE table:items +### ADD allowance FROM table:item_reductions WHEN id.value == @id.value; +# diff --git a/org.xalgorithms.examples.qc_gas_tax/reductions_by_distance.json b/org.xalgorithms.examples.qc_gas_tax/reductions_by_distance.json new file mode 100644 index 0000000..c632c67 --- /dev/null +++ b/org.xalgorithms.examples.qc_gas_tax/reductions_by_distance.json @@ -0,0 +1,7 @@ +[ + { "distance" : 20, "reduction" : 0.00 }, + { "distance" : 15, "reduction" : 0.02 }, + { "distance" : 10, "reduction" : 0.04 }, + { "distance" : 5, "reduction" : 0.06 }, + { "distance" : 0, "reduction" : 0.08 } +] diff --git a/org.xalgorithms.examples.qc_gas_tax/reductions_by_distance.table b/org.xalgorithms.examples.qc_gas_tax/reductions_by_distance.table new file mode 100644 index 0000000..bc39542 --- /dev/null +++ b/org.xalgorithms.examples.qc_gas_tax/reductions_by_distance.table @@ -0,0 +1,6 @@ +META + VERSION "0.0.1" + MANAGER "Joseph Potvin " + MAINTAINER "Don Kelly "; + +DATA reductions_by_distance.json 01s5vg2x4tr142gd320zy15tka67h722; diff --git a/org.xalgorithms.examples.qc_gas_tax/supplier_distances.json b/org.xalgorithms.examples.qc_gas_tax/supplier_distances.json new file mode 100644 index 0000000..7e6deb6 --- /dev/null +++ b/org.xalgorithms.examples.qc_gas_tax/supplier_distances.json @@ -0,0 +1,8 @@ +[ + { "supplier_id" : "YJ4gh6Ofdq", "seller_distance" : 2.2 }, + { "supplier_id" : "UsN8EB1QZU", "seller_distance" : 15.7 }, + { "supplier_id" : "uZz9cKtwhx", "seller_distance" : 0.4 }, + { "supplier_id" : "HcfUd49gPV", "seller_distance" : 12.2 }, + { "supplier_id" : "0DXKYXBZr8", "seller_distance" : 7.8 }, + { "supplier_id" : "nIT8sF9vS4", "seller_distance" : 24.3 } +] diff --git a/org.xalgorithms.examples.qc_gas_tax/supplier_distances.table b/org.xalgorithms.examples.qc_gas_tax/supplier_distances.table new file mode 100644 index 0000000..a5dd8b8 --- /dev/null +++ b/org.xalgorithms.examples.qc_gas_tax/supplier_distances.table @@ -0,0 +1,6 @@ +META + VERSION "0.0.1" + MANAGER "Joseph Potvin " + MAINTAINER "Don Kelly "; + +DATA supplier_distances.json 152t1p6nhu85m5d9pi3qne75ni2g8xz4;