-
+
+
+
diff --git a/backend/lib/azimutt_web/templates/website/resources.html.heex b/backend/lib/azimutt_web/templates/website/resources.html.heex
new file mode 100644
index 000000000..119685d2e
--- /dev/null
+++ b/backend/lib/azimutt_web/templates/website/resources.html.heex
@@ -0,0 +1,23 @@
+
+
+
+
Resources
+
Some images you can use in Azimutt layouts.
+
+
+
+ <%= for path <- Path.wildcard("priv/static/images/resources/**/*") |> Enum.filter(&File.regular?/1) |> Enum.sort() do %>
+ -
+
+
+ <% end %>
+
+
+
+
diff --git a/backend/priv/static/images/resources/arrows-black-fat/down.png b/backend/priv/static/images/resources/arrows-black-fat/down.png
new file mode 100644
index 000000000..4db7da753
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-black-fat/down.png differ
diff --git a/backend/priv/static/images/resources/arrows-black-fat/left-3.png b/backend/priv/static/images/resources/arrows-black-fat/left-3.png
new file mode 100644
index 000000000..18939554d
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-black-fat/left-3.png differ
diff --git a/backend/priv/static/images/resources/arrows-black-fat/left-4.png b/backend/priv/static/images/resources/arrows-black-fat/left-4.png
new file mode 100644
index 000000000..7bfb82954
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-black-fat/left-4.png differ
diff --git a/backend/priv/static/images/resources/arrows-black-fat/left.png b/backend/priv/static/images/resources/arrows-black-fat/left.png
new file mode 100644
index 000000000..1d5c7aa9e
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-black-fat/left.png differ
diff --git a/backend/priv/static/images/resources/arrows-black-fat/right-2.png b/backend/priv/static/images/resources/arrows-black-fat/right-2.png
new file mode 100644
index 000000000..67fc4e4bc
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-black-fat/right-2.png differ
diff --git a/backend/priv/static/images/resources/arrows-black-fat/right-3.png b/backend/priv/static/images/resources/arrows-black-fat/right-3.png
new file mode 100644
index 000000000..60f397d78
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-black-fat/right-3.png differ
diff --git a/backend/priv/static/images/resources/arrows-black-fat/right-4.png b/backend/priv/static/images/resources/arrows-black-fat/right-4.png
new file mode 100644
index 000000000..da3bff7da
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-black-fat/right-4.png differ
diff --git a/backend/priv/static/images/resources/arrows-black-fat/right-5.png b/backend/priv/static/images/resources/arrows-black-fat/right-5.png
new file mode 100644
index 000000000..d3e77039e
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-black-fat/right-5.png differ
diff --git a/backend/priv/static/images/resources/arrows-black-fat/right-6.png b/backend/priv/static/images/resources/arrows-black-fat/right-6.png
new file mode 100644
index 000000000..67510d0c5
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-black-fat/right-6.png differ
diff --git a/backend/priv/static/images/resources/arrows-black-fat/right.png b/backend/priv/static/images/resources/arrows-black-fat/right.png
new file mode 100644
index 000000000..425f686ac
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-black-fat/right.png differ
diff --git a/backend/priv/static/images/resources/arrows-black-fat/up-2.png b/backend/priv/static/images/resources/arrows-black-fat/up-2.png
new file mode 100644
index 000000000..429eb4331
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-black-fat/up-2.png differ
diff --git a/backend/priv/static/images/resources/arrows-black-fat/up.png b/backend/priv/static/images/resources/arrows-black-fat/up.png
new file mode 100644
index 000000000..9cd5ffa3c
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-black-fat/up.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/down-2.png b/backend/priv/static/images/resources/arrows-gold/down-2.png
new file mode 100644
index 000000000..27e40e840
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/down-2.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/down-3.png b/backend/priv/static/images/resources/arrows-gold/down-3.png
new file mode 100644
index 000000000..a7e14924f
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/down-3.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/down.png b/backend/priv/static/images/resources/arrows-gold/down.png
new file mode 100644
index 000000000..51fd66a17
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/down.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/left.png b/backend/priv/static/images/resources/arrows-gold/left.png
new file mode 100644
index 000000000..c5c271a6a
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/left.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/right-2.png b/backend/priv/static/images/resources/arrows-gold/right-2.png
new file mode 100644
index 000000000..f4ddabffc
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/right-2.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/right-3.png b/backend/priv/static/images/resources/arrows-gold/right-3.png
new file mode 100644
index 000000000..5bb553bf1
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/right-3.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/right-4.png b/backend/priv/static/images/resources/arrows-gold/right-4.png
new file mode 100644
index 000000000..5900627bb
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/right-4.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/right-5.png b/backend/priv/static/images/resources/arrows-gold/right-5.png
new file mode 100644
index 000000000..8236b15b8
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/right-5.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/right.png b/backend/priv/static/images/resources/arrows-gold/right.png
new file mode 100644
index 000000000..eef066dec
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/right.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/up-2.png b/backend/priv/static/images/resources/arrows-gold/up-2.png
new file mode 100644
index 000000000..05460304d
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/up-2.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/up-3.png b/backend/priv/static/images/resources/arrows-gold/up-3.png
new file mode 100644
index 000000000..55a1c48d9
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/up-3.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/up-4.png b/backend/priv/static/images/resources/arrows-gold/up-4.png
new file mode 100644
index 000000000..0ecce5602
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/up-4.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/up-5.png b/backend/priv/static/images/resources/arrows-gold/up-5.png
new file mode 100644
index 000000000..741ab7e90
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/up-5.png differ
diff --git a/backend/priv/static/images/resources/arrows-gold/up.png b/backend/priv/static/images/resources/arrows-gold/up.png
new file mode 100644
index 000000000..d05258b0d
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-gold/up.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/blue-down.png b/backend/priv/static/images/resources/arrows-highlighter/blue-down.png
new file mode 100644
index 000000000..9d627fafc
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/blue-down.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/blue-left.png b/backend/priv/static/images/resources/arrows-highlighter/blue-left.png
new file mode 100644
index 000000000..dbe528e8d
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/blue-left.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/blue-up-2.png b/backend/priv/static/images/resources/arrows-highlighter/blue-up-2.png
new file mode 100644
index 000000000..a9b28c5e1
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/blue-up-2.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/blue-up.png b/backend/priv/static/images/resources/arrows-highlighter/blue-up.png
new file mode 100644
index 000000000..49683fc56
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/blue-up.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/green-down.png b/backend/priv/static/images/resources/arrows-highlighter/green-down.png
new file mode 100644
index 000000000..606f02662
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/green-down.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/green-right-2.png b/backend/priv/static/images/resources/arrows-highlighter/green-right-2.png
new file mode 100644
index 000000000..e23d8634d
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/green-right-2.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/green-right.png b/backend/priv/static/images/resources/arrows-highlighter/green-right.png
new file mode 100644
index 000000000..18a7af286
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/green-right.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/orange-down.png b/backend/priv/static/images/resources/arrows-highlighter/orange-down.png
new file mode 100644
index 000000000..88df2186c
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/orange-down.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/orange-top-right.png b/backend/priv/static/images/resources/arrows-highlighter/orange-top-right.png
new file mode 100644
index 000000000..0da228a8a
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/orange-top-right.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/orange-top.png b/backend/priv/static/images/resources/arrows-highlighter/orange-top.png
new file mode 100644
index 000000000..3f948cd00
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/orange-top.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/purple-down-2.png b/backend/priv/static/images/resources/arrows-highlighter/purple-down-2.png
new file mode 100644
index 000000000..bb10d3804
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/purple-down-2.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/purple-down.png b/backend/priv/static/images/resources/arrows-highlighter/purple-down.png
new file mode 100644
index 000000000..ff35ce075
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/purple-down.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/purple-left.png b/backend/priv/static/images/resources/arrows-highlighter/purple-left.png
new file mode 100644
index 000000000..2b2edf108
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/purple-left.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/purple-right-2.png b/backend/priv/static/images/resources/arrows-highlighter/purple-right-2.png
new file mode 100644
index 000000000..68feda56d
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/purple-right-2.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/purple-right.png b/backend/priv/static/images/resources/arrows-highlighter/purple-right.png
new file mode 100644
index 000000000..1b25e9dc6
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/purple-right.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/yellow-left.png b/backend/priv/static/images/resources/arrows-highlighter/yellow-left.png
new file mode 100644
index 000000000..cbe5c7227
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/yellow-left.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/yellow-right.png b/backend/priv/static/images/resources/arrows-highlighter/yellow-right.png
new file mode 100644
index 000000000..35cd1a6cb
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/yellow-right.png differ
diff --git a/backend/priv/static/images/resources/arrows-highlighter/yellow-up.png b/backend/priv/static/images/resources/arrows-highlighter/yellow-up.png
new file mode 100644
index 000000000..ec30ddc5e
Binary files /dev/null and b/backend/priv/static/images/resources/arrows-highlighter/yellow-up.png differ
diff --git a/backend/priv/static/images/screenshots/azimutt-ecommerce.png b/backend/priv/static/images/screenshots/azimutt-ecommerce.png
new file mode 100644
index 000000000..989db7da7
Binary files /dev/null and b/backend/priv/static/images/screenshots/azimutt-ecommerce.png differ
diff --git a/backend/priv/static/images/screenshots/azimutt.png b/backend/priv/static/images/screenshots/azimutt-gospeak.png
similarity index 100%
rename from backend/priv/static/images/screenshots/azimutt.png
rename to backend/priv/static/images/screenshots/azimutt-gospeak.png
diff --git a/cli/package.json b/cli/package.json
index 97ba39373..c98a83e0d 100644
--- a/cli/package.json
+++ b/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "azimutt",
- "version": "0.1.24",
+ "version": "0.1.25",
"description": "Export database schema from relational or document databases. Import it to https://azimutt.app",
"keywords": [
"database",
diff --git a/cli/src/version.ts b/cli/src/version.ts
index 3a0a2277d..c8c19954a 100644
--- a/cli/src/version.ts
+++ b/cli/src/version.ts
@@ -1 +1 @@
-export const version = '0.1.24' // FIXME: `process.env.npm_package_version` is not available :/
+export const version = '0.1.25' // FIXME: `process.env.npm_package_version` is not available :/
diff --git a/demos/ecommerce/README.md b/demos/ecommerce/README.md
new file mode 100644
index 000000000..63ab99eca
--- /dev/null
+++ b/demos/ecommerce/README.md
@@ -0,0 +1,23 @@
+# E-commerce demo
+
+This is a medium demo (~80 tables) showcasing Azimutt ability to explore large schemas, even with several databases (micro-services for examples).
+
+You can find this project directly on [Azimutt](https://azimutt.app/45f571a6-d9b8-4752-8a13-93ac0d2b7984/c00d0c45-8db2-46b7-9b51-eba661640c3c?token=59166798-32de-4f46-a1b4-0f7327a91336) and explore it yourself, you will see:
+
+- an overview of the schema across several databases
+- showcases per domain with sample rows
+- documentation layouts
+
+The project will let you access the schema and loaded data, but if you want to dig into the data, you will have to set up the databases you want using Docker:
+
+- **Referential** needs [SQL Server](../../libs/connector-sqlserver/README.md#local-setup) with [referential script](./source_01_referential_sqlserver.sql) (url `sqlserver://sa:azimutt_42@localhost:1433/Referential`)
+- **Identity** needs [MariaDB](../../libs/connector-mariadb/README.md#local-setup) with [identity script](./source_02_identity_mariadb.sql) (url `mariadb://root:mariadb@localhost:3307/identity`)
+- **Inventory** needs [Oracle](../../libs/connector-oracle/README.md#local-setup) with [inventory script](./source_03_inventory_oracle.sql) (url `oracle:thin:C##INVENTORY/inventory@localhost:1521/FREE`)
+- **Catalog** needs [PostgreSQL](../../libs/connector-postgres/README.md#local-setup) with [catalog script](./source_04_catalog_postgres.sql) (url `postgresql://postgres:postgres@localhost:5433/postgres?schema=catalog`)
+- **Shopping** needs [PostgreSQL](../../libs/connector-postgres/README.md#local-setup) with [shopping script](./source_05_shopping_postgres.sql) (url `postgresql://postgres:postgres@localhost:5433/postgres?schema=shopping`)
+- **Billing** needs [SQL Server](../../libs/connector-sqlserver/README.md#local-setup) with [billing script](./source_06_billing_sqlserver.sql) (url `sqlserver://sa:azimutt_42@localhost:1433/Billing`)
+- **Shipping** needs [MongoDB](../../libs/connector-mongodb/README.md#local-setup) with [shipping script](./source_07_shipping_mongo.sql) (url `mongodb://localhost:27017/shipping`)
+- **CRM** needs [MySQL](../../libs/connector-mysql/README.md#local-setup) with [crm script](./source_08_crm_mysql.sql) (url `mysql://root:mysql@localhost:3306/crm`)
+- **Analytics** needs [MongoDB](../../libs/connector-mongodb/README.md#local-setup) with [analytics script](./source_09_analytics_mongo.sql) (url `mongodb://localhost:27017/analytics`)
+
+Once your database(s) are set up, you can launch the local gateway (`npx azimutt@latest gateway`) and navigate the project data or set up your own.
diff --git a/demos/ecommerce/source_00_design.md b/demos/ecommerce/source_00_design.md
new file mode 100644
index 000000000..41e97b046
--- /dev/null
+++ b/demos/ecommerce/source_00_design.md
@@ -0,0 +1,822 @@
+# Referential
+
+referential.Countries | needs to be referenced for legal reasons
+ CountryId bigint pk
+ Code varchar unique
+ Name varchar unique
+ CreatedAt timestamp
+ DeletedAt timestamp nullable
+
+referential.States | used for auto-competes
+ StateId bigint pk
+ CountryId bigint fk referential.Countries.CountryId
+ Code varchar index
+ Name varchar index
+ CreatedAt timestamp
+ DeletedAt timestamp nullable
+
+referential.Cities | used for auto-competes
+ CityId bigint pk
+ StateId bigint fk referential.States.StateId
+ Name varchar index
+ CreatedAt timestamp
+ DeletedAt timestamp nullable
+
+# Identity
+
+identity.Users
+ id bigint pk
+ first_name varchar index=name
+ last_name varchar index=name
+ username varchar unique
+ email varchar unique
+ settings json
+ created_at timestamp
+ updated_at timestamp
+ deleted_at timestamp nullable index
+
+identity.Credentials
+ user_id bigint pk fk identity.Users.id
+ provider auth_provider(password, google, linkedin, facebook, twitter) pk | the used provider
+ provider_id varchar pk | the user id from the provider, in case of password, stores the hashed password with the salt
+ provider_data json nullable
+ used_last timestamp
+ used_count int
+ created_at timestamp
+ updated_at timestamp | for password change mostly
+
+identity.PasswordResets
+ id bigint pk
+ email varchar index
+ token varchar index | the key sent by email to allow to change the password without being logged
+ requested_at timestamp
+ expire_at timestamp
+ used_at timestamp nullable
+
+identity.Devices | a device is a browser tagged by a random id in its session
+ id bigint pk
+ sid uuid unique | a unique id stored in the browser to track it when not logged
+ user_agent varchar
+ created_at timestamp | first time this device is seen
+
+identity.UserDevices | created on user login to know which users are using which devices
+ user_id bigint pk fk identity.Users.id
+ device_id bigint pk fk identity.Devices.id
+ linked_at timestamp | on login
+ unlinked_at timestamp nullable | on logout
+
+identity.TrustedDevices | users can add a device to their trusted ones, so they will have longer session and less security validations
+ user_id bigint pk fk identity.Users.id
+ device_id bigint fk identity.Devices.id
+ name varchar nullable
+ kind device_kind(desktop, tablet, phone) nullable
+ usage device_usage(perso, pro) nullable
+ used_last timestamp
+ created_at timestamp
+ deleted_at timestamp nullable index
+
+identity.AuthLogs
+ id bigint pk
+ user_id bigint nullable fk identity.Users.id
+ email varchar nullable
+ event auth_event(signup, login_success, login_failure, password_reset_asked, password_reset_used)
+ ip varchar
+ ip_location geography nullable
+ user_agent varchar
+ device_id bigint fk identity.Devices.id
+ created_at timestamp
+
+# Inventory
+
+C##INVENTORY.EMPLOYEES
+ ID BIGINT pk
+ FIRST_NAME VARCHAR index=name
+ LAST_NAME VARCHAR index=name
+ EMAIL VARCHAR nullable index
+ PHONE VARCHAR nullable
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+ DELETED_AT TIMESTAMP nullable index
+ DELETED_BY BIGINT nullable fk identity.Users.id
+
+C##INVENTORY.WAREHOUSES
+ ID BIGINT pk
+ NAME VARCHAR unique
+ ADDRESS GLOBAL_ADDRESS
+ MANAGER BIGINT fk C##INVENTORY.EMPLOYEES.ID
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+ DELETED_AT TIMESTAMP nullable index
+ DELETED_BY BIGINT nullable fk identity.Users.id
+
+C##INVENTORY.HALLS
+ ID BIGINT pk
+ WAREHOUSE_ID BIGINT fk C##INVENTORY.WAREHOUSES.ID
+ NAME VARCHAR index
+ MANAGER BIGINT fk C##INVENTORY.EMPLOYEES.ID
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+ DELETED_AT TIMESTAMP nullable index
+ DELETED_BY BIGINT nullable fk identity.Users.id
+
+C##INVENTORY.AISLES
+ ID BIGINT pk
+ HALL_ID BIGINT fk C##INVENTORY.HALLS.ID
+ NAME VARCHAR index
+ MANAGER BIGINT fk C##INVENTORY.EMPLOYEES.ID
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+ DELETED_AT TIMESTAMP nullable index
+ DELETED_BY BIGINT nullable fk identity.Users.id
+
+C##INVENTORY.RACKS
+ ID BIGINT pk
+ AISLE_ID BIGINT fk C##INVENTORY.AISLES.ID
+ NAME VARCHAR index
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+ DELETED_AT TIMESTAMP nullable index
+ DELETED_BY BIGINT nullable fk identity.Users.id
+
+C##INVENTORY.SHELVES
+ ID BIGINT pk
+ RACK_ID BIGINT fk C##INVENTORY.RACKS.ID
+ NAME VARCHAR index
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+ DELETED_AT TIMESTAMP nullable index
+ DELETED_BY BIGINT nullable fk identity.Users.id
+
+C##INVENTORY.SHELF_POSITIONS
+ ID BIGINT pk
+ SHELF_ID BIGINT fk C##INVENTORY.SHELVES.ID
+ NAME VARCHAR index
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+ DELETED_AT TIMESTAMP nullable index
+ DELETED_BY BIGINT nullable fk identity.Users.id
+
+C##INVENTORY.WAREHOUSE_EMPLOYEES
+ WAREHOUSE_ID BIGINT pk fk C##INVENTORY.WAREHOUSES.ID
+ EMPLOYEE_ID BIGINT pk fk C##INVENTORY.EMPLOYEES.ID
+ ROLE warehouse_role(manager, stocker, loader, receiver)
+ START TIMESTAMP nullable
+ END TIMESTAMP nullable
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+ DELETED_AT TIMESTAMP nullable index
+ DELETED_BY BIGINT nullable fk identity.Users.id
+
+C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS | how to check the employee is identified, can be several
+ WAREHOUSE_ID BIGINT pk fk C##INVENTORY.WAREHOUSES.ID
+ EMPLOYEE_ID BIGINT pk fk C##INVENTORY.EMPLOYEES.ID
+ KIND identity_proof_kind(name, cni, badge) pk
+ VALUE VARCHAR
+ EXPIRE TIMESTAMP nullable
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+
+C##INVENTORY.BRANDS
+ ID BIGINT pk
+ SLUG VARCHAR unique | ex: "google"
+ NAME VARCHAR unique | ex: "Google"
+ CREATED_AT TIMESTAMP
+ UPDATED_AT TIMESTAMP
+ DELETED_AT TIMESTAMP nullable index
+
+C##INVENTORY.PRODUCTS
+ ID BIGINT pk
+ SLUG VARCHAR unique | ex: "pixel-8-pro"
+ NAME VARCHAR unique | ex: "Pixel 8 Pro"
+ BRAND BIGINT nullable fk C##INVENTORY.BRANDS.ID
+ CATEGORY VARCHAR nullable | ex: "Phones"
+ SUBCATEGORY VARCHAR nullable | ex: "Smartphones"
+ WIDTH FLOAT | typical width of the product in millis, see PRODUCT_VERSIONS for the real one
+ LENGTH FLOAT | typical length of the product in millis, see PRODUCT_VERSIONS for the real one
+ HEIGHT FLOAT | typical height of the product in millis, see PRODUCT_VERSIONS for the real one
+ WEIGHT FLOAT | typical weight of the product in grams, see PRODUCT_VERSIONS for the real one
+ REMARKS TEXT nullable | ex: fragile
+ CREATED_AT TIMESTAMP
+ UPDATED_AT TIMESTAMP
+ DELETED_AT TIMESTAMP nullable index
+
+C##INVENTORY.PRODUCT_VERSIONS
+ ID BIGINT pk
+ PRODUCT_ID BIGINT fk C##INVENTORY.PRODUCTS.ID
+ SKU VARCHAR(12) unique | internal id
+ EAN VARCHAR(13) unique | european id
+ NAME VARCHAR unique | ex: "Pixel 8 Pro Menthe 128 Go"
+ SPECS JSON | specificities of this version, ex: `{color: "Menthe", storage: 128}`
+ WIDTH FLOAT | in millis
+ LENGTH FLOAT | in millis
+ HEIGHT FLOAT | in millis
+ WEIGHT FLOAT | in grams
+ REMARKS TEXT nullable
+ CREATED_AT TIMESTAMP
+ UPDATED_AT TIMESTAMP
+ DELETED_AT TIMESTAMP nullable index
+
+C##INVENTORY.PHYSICAL_PRODUCTS
+ ID BIGINT pk
+ PRODUCT_VERSION_ID BIGINT fk C##INVENTORY.PRODUCT_VERSIONS.ID
+ SNID VARCHAR(12) unique | serial number of this product
+ EXPIRATION TIMESTAMP nullable | when Product has an expiration date, null otherwise
+ REMARKS TEXT nullable
+ STORED BIGINT nullable fk C##INVENTORY.SHELF_POSITIONS.ID
+ CREATED_AT TIMESTAMP
+ UPDATED_AT TIMESTAMP
+ DELETED_AT TIMESTAMP nullable index
+
+C##INVENTORY.SUPPLIERS
+ ID BIGINT pk
+ NAME VARCHAR index
+ LEVEL INT | the lower, the more priority is given to this supplier
+ CURRENCY global_currency(EUR, USD)
+ CREATED_AT TIMESTAMP
+ UPDATED_AT TIMESTAMP
+ DELETED_AT TIMESTAMP nullable index
+
+C##INVENTORY.SUPPLIER_PRICES
+ SUPPLIER_ID BIGINT pk fk C##INVENTORY.SUPPLIERS.ID
+ PRODUCT_VERSION_ID BIGINT pk fk C##INVENTORY.PRODUCT_VERSIONS.ID
+ PRICE DOUBLE
+ CREATED_AT TIMESTAMP
+ UPDATED_AT TIMESTAMP
+ DELETED_AT TIMESTAMP nullable index
+
+C##INVENTORY.SUPPLIER_EMPLOYEES
+ SUPPLIER_ID BIGINT pk fk C##INVENTORY.SUPPLIERS.ID
+ EMPLOYEE_ID BIGINT pk fk C##INVENTORY.Employees.ID
+ ROLE supplier_role(delivery, sale)
+ START TIMESTAMP nullable
+ END TIMESTAMP nullable
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+ DELETED_AT TIMESTAMP nullable index
+ DELETED_BY BIGINT nullable fk identity.Users.id
+
+C##INVENTORY.PURCHASE_ORDERS
+ ID BIGINT pk
+ SUPPLIER_ID BIGINT fk C##INVENTORY.SUPPLIERS.ID
+ PRICE DOUBLE | total price, computed from items price x quantity
+ CURRENCY global_currency(EUR, USD)
+ DETAILS TEXT nullable | additional text for the supplier
+ NOTES TEXT nullable | internal text for employees
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+ SENT_AT TIMESTAMP nullable | can't be updated once sent
+ SENT_BY BIGINT nullable fk identity.Users.id
+ PAID_AT TIMESTAMP nullable
+ DELIVERED_AT TIMESTAMP nullable
+ VALIDATED_AT TIMESTAMP nullable
+ VALIDATED_BY BIGINT nullable fk identity.Users.id
+
+C##INVENTORY.PURCHASE_ORDER_ITEMS
+ PURCHASE_ORDER_ID BIGINT pk fk C##INVENTORY.PURCHASE_ORDERS.ID
+ PRODUCT_VERSION_ID BIGINT pk fk C##INVENTORY.PRODUCT_VERSIONS.ID
+ QUANTITY INT
+ PRICE DOUBLE
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+
+C##INVENTORY.DELIVERIES
+ ID BIGINT pk
+ REASON delivery_reason(supplier_delivery, customer_return, other)
+ ACCEPTED BOOLEAN
+ PURCHASE_ORDER_ID BIGINT nullable fk C##INVENTORY.PURCHASE_ORDERS.ID
+ WAREHOUSE_ID BIGINT fk C##INVENTORY.WAREHOUSE_EMPLOYEES.WAREHOUSE_ID
+ WAREHOUSE_EMPLOYEE_ID BIGINT fk C##INVENTORY.WAREHOUSE_EMPLOYEES.EMPLOYEE_ID
+ SUPPLIER_ID BIGINT fk C##INVENTORY.SUPPLIER_EMPLOYEES.SUPPLIER_ID
+ SUPPLIER_EMPLOYEE_ID BIGINT fk C##INVENTORY.SUPPLIER_EMPLOYEES.EMPLOYEE_ID
+ DELIVERED_AT TIMESTAMP
+
+C##INVENTORY.DELIVERY_ITEMS
+ DELIVERY_ID BIGINT pk fk C##INVENTORY.DELIVERIES.ID
+ PHYSICAL_PRODUCT_ID BIGINT pk fk C##INVENTORY.PHYSICAL_PRODUCTS.ID
+
+C##INVENTORY.PICKUPS
+ ID BIGINT pk
+ REASON pickup_reason(customer_delivery, supplier_return, other)
+ ACCEPTED BOOLEAN
+ WAREHOUSE_ID BIGINT fk C##INVENTORY.WAREHOUSE_EMPLOYEES.WAREHOUSE_ID
+ WAREHOUSE_EMPLOYEE_ID BIGINT fk C##INVENTORY.WAREHOUSE_EMPLOYEES.EMPLOYEE_ID
+ SUPPLIER_ID BIGINT fk C##INVENTORY.SUPPLIER_EMPLOYEES.SUPPLIER_ID
+ SUPPLIER_EMPLOYEE_ID BIGINT fk C##INVENTORY.SUPPLIER_EMPLOYEES.EMPLOYEE_ID
+ DELIVERED_AT TIMESTAMP
+
+C##INVENTORY.PICKUP_ITEMS
+ PICKUP_ID BIGINT pk fk C##INVENTORY.PICKUPS.ID
+ PHYSICAL_PRODUCT_ID BIGINT pk fk C##INVENTORY.PHYSICAL_PRODUCTS.ID
+
+C##INVENTORY.INVENTORIES
+ ID BIGINT pk
+ NAME VARCHAR
+ WAREHOUSE_ID BIGINT fk C##INVENTORY.WAREHOUSES.ID
+ HALL_ID BIGINT nullable fk C##INVENTORY.HALLS.ID
+ AISLE_ID BIGINT nullable fk C##INVENTORY.AISLES.ID
+ PLANNED TIMESTAMP nullable
+ FINISHED TIMESTAMP nullable
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+
+C##INVENTORY.INVENTORY_MEMBERS
+ INVENTORY_ID BIGINT pk fk C##INVENTORY.INVENTORIES.ID
+ WAREHOUSE_ID BIGINT pk fk C##INVENTORY.WAREHOUSE_EMPLOYEES.WAREHOUSE_ID
+ EMPLOYEE_ID BIGINT pk fk C##INVENTORY.WAREHOUSE_EMPLOYEES.EMPLOYEE_ID
+
+C##INVENTORY.INVENTORY_OBSERVATIONS
+ ID BIGINT pk
+ INVENTORY_ID BIGINT nullable fk C##INVENTORY.INVENTORIES.ID
+ PHYSICAL_PRODUCT_ID BIGINT fk C##INVENTORY.PHYSICAL_PRODUCTS.ID
+ STATUS INVENTORY_STATUS(missing, broken, degraded)
+ MESSAGE TEXT nullable
+ CREATED_AT TIMESTAMP
+ CREATED_BY BIGINT fk identity.Users.id
+ UPDATED_AT TIMESTAMP
+ UPDATED_BY BIGINT fk identity.Users.id
+ DELETED_AT TIMESTAMP nullable index
+ DELETED_BY BIGINT nullable fk identity.Users.id
+
+# Catalog
+
+catalog.categories
+ id bigint pk
+ parent bigint nullable fk catalog.categories.id
+ depth int | easily accessible information of number of parents
+ slug varchar unique
+ name varchar
+ description text nullable
+ description_html text nullable
+ created_at timestamp
+ updated_at timestamp
+ deleted_at timestamp nullable index
+
+catalog.products
+ id bigint pk fk C##INVENTORY.PRODUCTS.ID
+ slug varchar unique
+ name varchar
+ category_id bigint fk catalog.categories.id
+ description text nullable | TODO: handle i18n
+ description_html text nullable
+ versions json | ex: `[{key: "color", label: "Couleur", values: [{name: "Bleu Azur", value: "#95bbe2"}]}, {key: "storage", name: "Taille", values: [{name: "128GB", value: 128}]}]`
+ attributes json | ex: `[{key: "Marque", value: "Google"}]`
+ stock int | informative stock, may not be accurate
+ created_at timestamp
+ updated_at timestamp
+ deleted_at timestamp nullable index
+
+catalog.product_versions
+ id bigint pk fk C##INVENTORY.PRODUCT_VERSIONS.ID
+ product_id bigint fk catalog.products.id
+ name varchar
+ specs json | ex: `{color: "Bleu Azur", storage: 128}`
+ price double
+ stock int | informative stock, may not be accurate
+ created_at timestamp
+ updated_at timestamp
+ deleted_at timestamp nullable index
+
+catalog.product_cross_sell_options
+ product_id bigint pk fk catalog.products.id
+ product_version_id bigint pk fk catalog.product_versions.id
+ label varchar
+ created_at timestamp
+ updated_at timestamp
+ deleted_at timestamp nullable index
+
+catalog.product_alternatives
+ product_id bigint pk fk catalog.products.id
+ alternative_product_id bigint pk fk catalog.products.id
+ created_at timestamp
+ updated_at timestamp
+ deleted_at timestamp nullable index
+
+catalog.assets
+ id bigint pk
+ kind asset_kind(picture, video, embed)
+ format asset_format(1:1, 16:9)
+ size asset_size(low, medium, high, retina)
+ path varchar
+ alt varchar
+ width int
+ height int
+ weight int
+ created_at timestamp
+ updated_at timestamp
+ deleted_at timestamp nullable index
+
+catalog.category_assets
+ category_id bigint pk fk catalog.categories.id
+ asset_id bigint pk fk catalog.assets.id
+ placement category_asset_placement(banner, icon)
+ created_at timestamp
+ updated_at timestamp
+ deleted_at timestamp nullable index
+
+catalog.product_assets
+ product_id bigint pk fk catalog.products.id
+ asset_id bigint pk fk catalog.assets.id
+ placement category_asset_placement(banner, icon)
+ created_at timestamp
+ updated_at timestamp
+ deleted_at timestamp nullable index
+
+catalog.product_version_assets
+ product_version_id bigint pk fk catalog.product_versions.id
+ asset_id bigint pk fk catalog.assets.id
+ placement category_asset_placement(banner, icon)
+ created_at timestamp
+ updated_at timestamp
+ deleted_at timestamp nullable index
+
+catalog.product_reviews
+ id bigint pk
+ product_id bigint fk catalog.products.id
+ product_version_id bigint nullable fk catalog.product_versions.id
+ invoice_id bigint nullable fk billing.Invoices.InvoiceId
+ physical_product_id bigint nullable fk C##INVENTORY.PHYSICAL_PRODUCTS.ID
+ rating int
+ review text nullable
+ created_at timestamp
+ created_by bigint fk identity.Users.id
+ updated_at timestamp
+ updated_by bigint fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+
+catalog.product_review_assets
+ product_review_id bigint pk fk catalog.product_reviews.id
+ asset_id bigint pk fk catalog.assets.id
+ created_at timestamp
+ created_by bigint fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+
+catalog.product_review_feedbacks
+ product_review_id bigint pk fk catalog.product_reviews.id
+ kind feedback_kind(like, report)
+ created_at timestamp
+ created_by bigint pk fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+
+# Shopping
+
+shopping.carts
+ id bigint pk
+ owner_kind cart_owner(identity.Devices, identity.Users) | Devices are used for anonymous carts, otherwise it's Users
+ owner_id bigint
+ expire_at timestamp
+ created_at timestamp
+ updated_at timestamp
+ deleted_at timestamp nullable index
+fk shopping.carts.owner_id -> identity.Devices.id
+fk shopping.carts.owner_id -> identity.Users.id
+
+shopping.cart_items
+ cart_id bigint pk fk shopping.carts.id
+ product_version_id bigint pk fk catalog.product_versions.id
+ quantity int
+ price double | at the time the product was added to the card, prevent price changes after a product has been added to a cart
+ created_at timestamp
+ created_by bigint nullable fk identity.Users.id
+ updated_at timestamp
+ updated_by bigint nullable fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+
+shopping.wishlists
+ id bigint pk
+ name varchar
+ description text nullable
+ public boolean
+ created_at timestamp
+ created_by bigint fk identity.Users.id
+ updated_at timestamp
+ updated_by bigint fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+
+shopping.wishlist_items
+ wishlist_id bigint pk fk shopping.wishlists.id
+ product_id bigint pk fk catalog.products.id
+ specs json nullable | if the user saved specific configuration
+ created_at timestamp
+ created_by bigint fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+
+shopping.wishlist_members
+ wishlist_id bigint pk fk shopping.wishlists.id
+ user_id bigint pk fk identity.Users.id
+ rights wishlist_rights(edit, comment, view)
+ created_at timestamp
+ created_by bigint fk identity.Users.id
+ updated_at timestamp
+ updated_by bigint fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+
+# Billing
+
+billing.CustomerAddresses
+ CustomerAddressesId bigint pk
+ Name varchar
+ Street varchar
+ City varchar
+ State varchar
+ ZipCode varchar
+ Country bigint fk referential.Countries.CountryId
+ Complements text nullable
+ CreatedAt timestamp
+ CreatedBy bigint fk identity.Users.id
+ DeletedAt timestamp nullable index
+ DeletedBy bigint nullable fk identity.Users.id
+
+billing.Customers
+ CustomerId bigint pk
+ Name varchar
+ BillingAddress bigint nullable fk billing.CustomerAddresses.CustomerAddressesId
+ Siret varchar nullable
+ TVA varchar nullable
+ CreatedAt timestamp
+ CreatedBy bigint fk identity.Users.id
+ UpdatedAt timestamp
+ UpdatedBy bigint fk identity.Users.id
+ DeletedAt timestamp nullable index
+ DeletedBy bigint nullable fk identity.Users.id
+
+billing.CustomerMembers
+ CustomerId bigint pk fk billing.Customers.CustomerId
+ UserId bigint pk fk identity.Users.id
+ CanEdit boolean
+ CanInvite boolean
+ CanBuy boolean
+ BudgetAllowance int nullable
+ CreatedAt timestamp
+ CreatedBy bigint fk identity.Users.id
+ UpdatedAt timestamp
+ UpdatedBy bigint fk identity.Users.id
+ DeletedAt timestamp nullable index
+ DeletedBy bigint nullable fk identity.Users.id
+
+billing.CustomerPaymentMethods
+ CustomerPaymentMethodId bigint pk
+ CustomerId bigint fk billing.Customers.CustomerId
+ Name varchar
+ Kind payment_kind(card, paypal)
+ Details json
+ CreatedAt timestamp
+ CreatedBy bigint fk identity.Users.id
+ UpdatedAt timestamp
+ UpdatedBy bigint fk identity.Users.id
+ DeletedAt timestamp nullable index
+ DeletedBy bigint nullable fk identity.Users.id
+
+billing.Invoices
+ InvoiceId bigint pk
+ Reference varchar unique
+ CartId bigint nullable fk shopping.carts.id
+ CustomerId bigint fk billing.Customers.CustomerId
+ BillingAddress bigint fk billing.CustomerAddresses.CustomerAddressesId
+ TotalPrice double
+ Currency global_currency(EUR, USD)
+ PaidAt timestamp nullable
+ CreatedAt timestamp
+ CreatedBy bigint nullable fk identity.Users.id
+
+billing.InvoiceLines
+ InvoiceId bigint pk fk billing.Invoices.InvoiceId
+ Index int pk
+ ProductVersionId bigint nullable fk catalog.product_versions.id
+ Description text nullable
+ Price double
+ Quantity int
+
+billing.Payments
+ PaymentId bigint pk
+ InvoiceId bigint fk billing.Invoices.InvoiceId
+ PaymentMethodId bigint nullable fk billing.CustomerPaymentMethods.CustomerPaymentMethodId
+ Amount double
+ Currency global_currency(EUR, USD)
+ CreatedAt timestamp
+
+# Shipping
+
+shipping.Carriers
+ id bigint unique=pk
+ registration varchar index
+ licensePlate varchar
+ cargoWidth float | inner cargo width in millimeters
+ cargoLength float | inner cargo length in millimeters
+ cargoHeight float | inner cargo height in millimeters
+ cargoWeight float | maximum weight for the cargo, in kilograms
+ createdAt timestamp
+ createdBy bigint nullable fk identity.Users.id
+ updatedAt timestamp
+ updatedBy bigint nullable fk identity.Users.id
+ deletedAt timestamp nullable index
+ deletedBy bigint nullable fk identity.Users.id
+
+shipping.Shipments
+ id bigint unique=pk
+ carrierId bigint nullable fk shipping.Carriers.id
+ createdAt timestamp
+ collectedAt timestamp nullable
+ collectedBy bigint nullable fk identity.Users.id
+ packagedAt timestamp nullable
+ packagedBy bigint nullable fk identity.Users.id
+ loadedAt timestamp nullable
+ loadedBy bigint nullable fk identity.Users.id
+ deliveredAt timestamp nullable
+ deliveredBy bigint nullable fk identity.Users.id
+
+shipping.ShipmentItems
+ shipmentId bigint unique=pk fk shipping.Shipments.id
+ physicalProductId bigint unique=pk fk C##INVENTORY.PHYSICAL_PRODUCTS.ID
+ invoiceId bigint index fk billing.InvoiceLines.InvoiceId
+ invoiceLine int fk billing.InvoiceLines.Index
+ deliveredAt timestamp nullable
+ deliveredTo bigint nullable fk identity.Users.id | the User who got the delivered package
+
+# CRM
+
+crm.People
+ id bigint pk
+ name varchar
+ email varchar nullable
+ phone varchar nullable
+ created_at timestamp
+ created_by bigint nullable fk identity.Users.id
+ updated_at timestamp
+ updated_by bigint nullable fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+
+crm.Organizations
+ id bigint pk
+ name varchar
+ created_at timestamp
+ created_by bigint nullable fk identity.Users.id
+ updated_at timestamp
+ updated_by bigint nullable fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+
+crm.OrganizationMembers
+ person_id bigint pk fk crm.People.id
+ organization_id bigint pk fk crm.Organizations.id
+ role varchar
+ created_at timestamp
+ created_by bigint nullable fk identity.Users.id
+ updated_at timestamp
+ updated_by bigint nullable fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+
+crm.SocialAccounts
+ id bigint pk
+ network social_network(twitter, linkedin, facebook, instagram, tiktok, snapchat)
+ username varchar
+ owner_kind social_account_owner_kind(People, Organizations) nullable
+ owner_id bigint nullable
+ created_at timestamp
+ created_by bigint nullable fk identity.Users.id
+ updated_at timestamp
+ updated_by bigint nullable fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+fk crm.SocialAccounts.owner_id -> crm.People.id
+fk crm.SocialAccounts.owner_id -> crm.Organizations.id
+
+crm.Campaigns
+ id bigint pk
+ name varchar
+ status campaign_status(draft, live, paused)
+ starts timestamp nullable
+ ends timestamp nullable
+ kind campaign_kind(email, sms, push, twitter, linkedin, instagram, facebook)
+ audience text | DSL for selecting the audience, from crm.People for email & sms or from crm.SocialAccounts for others
+ subject varchar nullable
+ message text nullable | HTML with templating using recipient info
+ created_at timestamp
+ created_by bigint nullable fk identity.Users.id
+ updated_at timestamp
+ updated_by bigint nullable fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+
+crm.CampaignMessages
+ id bigint pk
+ campaign_id bigint fk crm.Campaigns.id
+ contact_id bigint fk crm.People.id
+ social_id bigint nullable fk crm.SocialAccounts.id
+ sent_to varchar | can be email, phone number, social account... depending on campaign kind
+ created_at timestamp
+ sent_at timestamp nullable
+ opened_at timestamp nullable
+ clicked_at timestamp nullable
+
+crm.Issues
+ id bigint pk
+ subject varchar
+ created_at timestamp
+ created_by bigint fk identity.Users.id
+ closed_at timestamp nullable index
+ closed_by bigint nullable fk identity.Users.id
+
+crm.IssueMessages
+ id bigint pk
+ issue_id bigint fk crm.Issues.id
+ content text
+ created_at timestamp
+ created_by bigint fk identity.Users.id
+ updated_at timestamp
+ updated_by bigint fk identity.Users.id
+
+crm.IssueMessageReactions
+ id bigint pk
+ message_id bigint fk crm.IssueMessages.id
+ kind reaction_kind(like, dislike)
+ created_at timestamp
+ created_by bigint fk identity.Users.id
+ deleted_at timestamp nullable index
+ deleted_by bigint nullable fk identity.Users.id
+
+crm.Discounts
+ id bigint pk
+ name varchar
+ description varchar
+ kind discount_kind(percentage, amount)
+ value double
+ enable_at timestamp nullable
+ expire_at timestamp nullable
+ created_at timestamp
+ created_by bigint fk identity.Users.id
+ updated_at timestamp
+ updated_by bigint fk identity.Users.id
+ deleted_at timestamp
+ deleted_by bigint fk identity.Users.id
+
+crm.Coupons
+ id bigint pk
+ discount_id bigint fk crm.Discounts.id
+ code varchar unique | public code to use the discount
+ expire_at timestamp nullable
+ created_at timestamp
+ created_by bigint fk identity.Users.id
+ updated_at timestamp
+ updated_by bigint fk identity.Users.id
+ deleted_at timestamp
+ deleted_by bigint fk identity.Users.id
+
+crm.ProductPicks
+crm.LoyaltyCards
+
+# Analytics
+
+analytics.Events
+ id uuid unique=pk | UUIDv7 to be time ordered
+ name string index | in form of `$context__$object__$action`
+ source event_source(website, app, admin, job) | the name of the system which emitted this event
+ details json nullable | any additional info for the event
+ entities json nullable | {[kind: string]: {id: string, name: string}[]}
+ createdAt timestamp index
+fk analytics.Events.entities:user:id -> identity.Users.id
+fk analytics.Events.entities:cart:id -> shopping.carts.id
+fk analytics.Events.entities:invoice:id -> billing.Invoices.InvoiceId
+
+analytics.Entities
+ kind string unique=pk
+ id string unique=pk
+ name string index
+ properties json
+ createdAt timestamp
+ updatedAt timestamp
+fk analytics.Entities.id -> identity.Users.id # when kind=user
+fk analytics.Entities.id -> shopping.carts.id # when kind=cart
+fk analytics.Entities.id -> billing.Invoices.InvoiceId # when kind=invoice
diff --git a/demos/ecommerce/source_01_referential_sqlserver.sql b/demos/ecommerce/source_01_referential_sqlserver.sql
new file mode 100644
index 000000000..85f30ea3f
--- /dev/null
+++ b/demos/ecommerce/source_01_referential_sqlserver.sql
@@ -0,0 +1,102 @@
+-- drop everything
+USE master; -- in order to stop using Referential ^^
+DROP DATABASE IF EXISTS Referential;
+
+-- create the database
+CREATE DATABASE Referential;
+USE Referential;
+CREATE SCHEMA [referential];
+
+
+CREATE TABLE [referential].[Countries] (
+ [CountryId] [bigint] IDENTITY (1,1) PRIMARY KEY,
+ [Code] [nvarchar](5) NOT NULL,
+ [Name] [nvarchar](255) NOT NULL,
+ [CreatedAt] [datetime] NOT NULL DEFAULT GETDATE(),
+ [DeletedAt] [datetime]
+);
+CREATE UNIQUE INDEX [IDX_Countries_Code] ON [referential].[Countries] ([Code]);
+CREATE UNIQUE INDEX [IDX_Countries_Name] ON [referential].[Countries] ([Name]);
+EXEC sp_addextendedproperty 'MS_Description', 'needs to be referenced for legal reasons', 'SCHEMA', 'referential', 'TABLE', 'Countries';
+
+CREATE TABLE [referential].[States] (
+ [StateId] [bigint] IDENTITY (1,1) PRIMARY KEY,
+ [CountryId] [bigint] NOT NULL,
+ [Code] [nvarchar](5) NOT NULL,
+ [Name] [nvarchar](255) NOT NULL,
+ [CreatedAt] [datetime] NOT NULL DEFAULT GETDATE(),
+ [DeletedAt] [datetime],
+ CONSTRAINT [FK_States_Country] FOREIGN KEY ([CountryId]) REFERENCES [referential].[Countries] ([CountryId])
+);
+CREATE INDEX [IDX_States_CountryId] ON [referential].[States] ([CountryId]);
+CREATE INDEX [IDX_States_Code] ON [referential].[States] ([Code]);
+CREATE INDEX [IDX_States_Name] ON [referential].[States] ([Name]);
+EXEC sp_addextendedproperty 'MS_Description', 'used for auto-competes', 'SCHEMA', 'referential', 'TABLE', 'States';
+
+CREATE TABLE [referential].[Cities] (
+ [CityId] [bigint] IDENTITY (1,1) PRIMARY KEY,
+ [StateId] [bigint] NOT NULL,
+ [Name] [nvarchar](255) NOT NULL,
+ [CreatedAt] [datetime] NOT NULL DEFAULT GETDATE(),
+ [DeletedAt] [datetime],
+ CONSTRAINT [FK_Cities_State] FOREIGN KEY ([StateId]) REFERENCES [referential].[States] ([StateId])
+);
+CREATE INDEX [IDX_Cities_StateId] ON [referential].[Cities] ([StateId]);
+CREATE INDEX [IDX_Cities_Name] ON [referential].[Cities] ([Name]);
+EXEC sp_addextendedproperty 'MS_Description', 'used for auto-competes', 'SCHEMA', 'referential', 'TABLE', 'Cities';
+
+
+-- insert some data
+INSERT INTO [referential].[Countries] ([Code], [Name])
+VALUES ('FRA', 'France'),
+ ('DEU', 'Germany'),
+ ('ESP', 'Spain'),
+ ('ITA', 'Italy'),
+ ('USA', 'United States'),
+ ('CAN', 'Canada'),
+ ('MEX', 'Mexico');
+
+INSERT INTO [referential].[States] ([CountryId], [Code], [Name])
+VALUES (1, 'IDF', N'Île-de-France'),
+ (1, 'PAC', N'Provence-Alpes-Côte d''Azur'),
+ (2, 'BE', 'Berlin'),
+ (2, 'BW', N'Baden-Württemberg'),
+ (3, 'MD', 'Madrid'),
+ (3, 'CAT', 'Catalonia'),
+ (4, 'LAZ', 'Lazio'),
+ (4, 'LOM', 'Lombardy'),
+ (5, 'CA', 'California'),
+ (5, 'TX', 'Texas'),
+ (5, 'NY', 'New York'),
+ (6, 'ON', 'Ontario'),
+ (6, 'QC', 'Quebec'),
+ (7, 'CDMX', N'Ciudad de México'),
+ (7, 'JAL', 'Jalisco');
+
+INSERT INTO [referential].[Cities] ([StateId], [Name])
+VALUES (1, 'Paris'),
+ (1, 'Boulogne-Billancourt'),
+ (2, 'Marseille'),
+ (2, 'Nice'),
+ (3, 'Berlin'),
+ (4, 'Stuttgart'),
+ (4, 'Karlsruhe'),
+ (5, 'Madrid'),
+ (6, 'Barcelona'),
+ (6, 'Girona'),
+ (7, 'Rome'),
+ (7, 'Latina'),
+ (8, 'Milan'),
+ (8, 'Bergamo'),
+ (9, 'Los Angeles'),
+ (9, 'San Francisco'),
+ (10, 'Houston'),
+ (10, 'Dallas'),
+ (11, 'New York City'),
+ (11, 'Buffalo'),
+ (12, 'Toronto'),
+ (12, 'Ottawa'),
+ (13, 'Montreal'),
+ (13, 'Quebec City'),
+ (14, 'Mexico City'),
+ (15, 'Guadalajara');
diff --git a/demos/ecommerce/source_02_identity_mariadb.sql b/demos/ecommerce/source_02_identity_mariadb.sql
new file mode 100644
index 000000000..062c7838b
--- /dev/null
+++ b/demos/ecommerce/source_02_identity_mariadb.sql
@@ -0,0 +1,291 @@
+-- drop everything
+DROP DATABASE IF EXISTS identity;
+
+-- create the database (needs admin rights)
+CREATE DATABASE identity;
+USE identity;
+
+
+CREATE TABLE identity.Users (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ first_name VARCHAR(255) NOT NULL,
+ last_name VARCHAR(255) NOT NULL,
+ username VARCHAR(255) NOT NULL UNIQUE,
+ email VARCHAR(255) NOT NULL UNIQUE,
+ settings JSON CHECK (JSON_VALID(settings)),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP,
+ INDEX idx_name (first_name, last_name),
+ INDEX idx_deleted_at (deleted_at)
+);
+
+CREATE TABLE identity.Credentials (
+ user_id BIGINT NOT NULL REFERENCES identity.Users (id),
+ provider ENUM ('password', 'google', 'linkedin', 'facebook', 'twitter') NOT NULL COMMENT 'the used provider',
+ provider_id VARCHAR(255) NOT NULL COMMENT 'the user id from the provider, in case of password, stores the hashed password with the salt',
+ provider_data JSON CHECK (JSON_VALID(provider_data)),
+ used_last TIMESTAMP,
+ used_count INT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'for password change mostly',
+ PRIMARY KEY (user_id, provider, provider_id)
+);
+
+CREATE TABLE identity.PasswordResets (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ email VARCHAR(255) NOT NULL,
+ token VARCHAR(255) NOT NULL COMMENT 'the key sent by email to allow to change the password without being logged',
+ requested_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ expire_at TIMESTAMP,
+ used_at TIMESTAMP,
+ INDEX idx_email (email),
+ INDEX idx_token (token)
+);
+
+CREATE TABLE identity.Devices (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ sid CHAR(36) NOT NULL UNIQUE COMMENT 'a unique id stored in the browser to track it when not logged',
+ user_agent VARCHAR(255) NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'first time this device is seen'
+) COMMENT 'a device is a browser tagged by a random id in its session';
+
+CREATE TABLE identity.UserDevices (
+ user_id BIGINT NOT NULL REFERENCES identity.Users (id),
+ device_id BIGINT NOT NULL REFERENCES identity.Devices (id),
+ linked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'on login',
+ unlinked_at TIMESTAMP COMMENT 'on logout',
+ PRIMARY KEY (user_id, device_id)
+) COMMENT 'created on user login to know which users are using which devices';
+
+CREATE TABLE identity.TrustedDevices (
+ user_id BIGINT NOT NULL REFERENCES identity.Users (id),
+ device_id BIGINT NOT NULL REFERENCES identity.Devices (id),
+ name VARCHAR(255),
+ kind ENUM ('desktop', 'tablet', 'phone'),
+ `usage` ENUM ('perso', 'pro'),
+ used_last TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP,
+ PRIMARY KEY (user_id, device_id),
+ INDEX idx_deleted_at (deleted_at)
+) COMMENT 'users can add a device to their trusted ones, so they will have longer session and less security validations';
+
+CREATE TABLE identity.AuthLogs (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ user_id BIGINT REFERENCES identity.Users (id),
+ email VARCHAR(255),
+ event ENUM ('signup', 'login_success', 'login_failure', 'password_reset_asked', 'password_reset_used') NOT NULL,
+ ip VARCHAR(45) NOT NULL,
+ ip_location POINT,
+ user_agent VARCHAR(255) NOT NULL,
+ device_id BIGINT NOT NULL REFERENCES identity.Devices (id),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+
+-- insert some data
+INSERT INTO identity.Users (id, first_name, last_name, username, email, settings)
+VALUES (1, 'John', 'Doe', 'johndoe', 'johndoe@example.com', '{"theme": "dark", "language": "en"}'),
+ (2, 'Jane', 'Doe', 'janedoe', 'janedoe@example.com', '{"theme": "light", "language": "en"}'),
+ (3, 'James', 'Bond', 'jamesbond', 'james.bond@mi6.co.uk', '{"theme": "dark", "language": "en"}'),
+ (4, 'Bruce', 'Wayne', 'brucewayne', 'bruce.wayne@wayneenterprises.com', '{"theme": "dark", "language": "en"}'),
+ (5, 'Clark', 'Kent', 'clarkkent', 'clark.kent@dailyplanet.com', '{"theme": "light", "language": "en"}'),
+ (6, 'Diana', 'Prince', 'dianaprince', 'diana.prince@themyscira.gov', '{"theme": "light", "language": "en"}'),
+ (7, 'Peter', 'Parker', 'peterparker', 'peter.parker@dailybugle.com', '{"theme": "light", "language": "en"}'),
+ (8, 'Tony', 'Stark', 'tonystark', 'tony.stark@starkindustries.com', '{"theme": "dark", "language": "en"}'),
+ (9, 'Natasha', 'Romanoff', 'natasharomanoff', 'natasha.romanoff@shield.gov', '{"theme": "dark", "language": "ru"}'),
+ (10, 'Steve', 'Rogers', 'steverogers', 'steve.rogers@avengers.com', '{"theme": "light", "language": "en"}'),
+ (11, 'Bruce', 'Banner', 'brucebanner', 'bruce.banner@avengers.com', '{"theme": "dark", "language": "en"}'),
+ (12, 'Wanda', 'Maximoff', 'wandamaximoff', 'wanda.maximoff@avengers.com', '{"theme": "dark", "language": "en"}'),
+ (13, 'Harry', 'Potter', 'harrypotter', 'harry.potter@hogwarts.ac.uk', '{"theme": "light", "language": "en"}'),
+ (14, 'Hermione', 'Granger', 'hermionegranger', 'hermione.granger@hogwarts.ac.uk', '{"theme": "light", "language": "en"}'),
+ (15, 'Ron', 'Weasley', 'ronweasley', 'ron.weasley@hogwarts.ac.uk', '{"theme": "light", "language": "en"}'),
+ (16, 'Albus', 'Dumbledore', 'albusdumbledore', 'albus.dumbledore@hogwarts.ac.uk', '{"theme": "dark", "language": "en"}'),
+ (17, 'Severus', 'Snape', 'severussnape', 'severus.snape@hogwarts.ac.uk', '{"theme": "dark", "language": "en"}'),
+ (18, 'Frodo', 'Baggins', 'frodobaggins', 'frodo.baggins@shire.me', '{"theme": "light", "language": "en"}'),
+ (19, 'Samwise', 'Gamgee', 'samwisegamgee', 'samwise.gamgee@shire.me', '{"theme": "light", "language": "en"}'),
+ (20, 'Gandalf', 'the Grey', 'gandalfthegrey', 'gandalf@middleearth.me', '{"theme": "dark", "language": "en"}'),
+ (21, 'Aragorn', 'Elessar', 'aragornelessar', 'aragorn@middleearth.me', '{"theme": "dark", "language": "en"}'),
+ (22, 'Legolas', 'Greenleaf', 'legolasgreenleaf', 'legolas@woodlandrealm.me', '{"theme": "light", "language": "en"}'),
+ (23, 'Gimli', 'Son of Glóin', 'gimlisonofgloin', 'gimli@lonelymountain.me', '{"theme": "dark", "language": "en"}'),
+ (24, 'Sherlock', 'Holmes', 'sherlockholmes', 'sherlock.holmes@bakerstreet.com', '{"theme": "dark", "language": "en"}'),
+ (25, 'John', 'Watson', 'johnwatson', 'john.watson@bakerstreet.com', '{"theme": "light", "language": "en"}'),
+ (26, 'Bilbo', 'Baggins', 'bilbobaggins', 'bilbo.baggins@shire.me', '{"theme": "light", "language": "en"}'),
+ (27, 'Tony', 'Montana', 'tonymontana', 'tony.montana@scarface.com', '{"theme": "dark", "language": "en"}'),
+ (28, 'Michael', 'Corleone', 'michaelcorleone', 'michael.corleone@corleone.com', '{"theme": "dark", "language": "en"}'),
+ (29, 'Vito', 'Corleone', 'vitocorleone', 'vito.corleone@corleone.com', '{"theme": "dark", "language": "it"}'),
+ (30, 'Ellen', 'Ripley', 'ellenripley', 'ellen.ripley@weylandyutani.com', '{"theme": "dark", "language": "en"}'),
+ (31, 'Sarah', 'Connor', 'sarahconnor', 'sarah.connor@skynet.com', '{"theme": "dark", "language": "en"}'),
+ (32, 'Neo', 'Anderson', 'neoanderson', 'neo.anderson@matrix.com', '{"theme": "dark", "language": "en"}'),
+ (33, 'Trinity', '', 'trinity', 'trinity@matrix.com', '{"theme": "dark", "language": "en"}'),
+ (34, 'Morpheus', '', 'morpheus', 'morpheus@matrix.com', '{"theme": "dark", "language": "en"}'),
+ (35, 'Ethan', 'Hunt', 'ethanhunt', 'ethan.hunt@imf.gov', '{"theme": "dark", "language": "en"}'),
+ (36, 'Indiana', 'Jones', 'indianajones', 'indiana.jones@archeology.com', '{"theme": "dark", "language": "en"}'),
+ (37, 'Han', 'Solo', 'hansolo', 'han.solo@rebellion.com', '{"theme": "dark", "language": "en"}'),
+ (38, 'Luke', 'Skywalker', 'lukeskywalker', 'luke.skywalker@rebellion.com', '{"theme": "light", "language": "en"}'),
+ (39, 'Leia', 'Organa', 'leiaorgana', 'leia.organa@rebellion.com', '{"theme": "light", "language": "en"}'),
+ (40, 'Yoda', '', 'yoda', 'yoda@jediorder.com', '{"theme": "dark", "language": "en"}'),
+ (41, 'Obi-Wan', 'Kenobi', 'obiwankenobi', 'obi-wan.kenobi@jediorder.com', '{"theme": "light", "language": "en"}'),
+ (42, 'Anakin', 'Skywalker', 'anakinskywalker', 'anakin.skywalker@jediorder.com', '{"theme": "dark", "language": "en"}'),
+ (43, 'Darth', 'Vader', 'darthvader', 'darth.vader@empire.com', '{"theme": "dark", "language": "en"}'),
+ (44, 'Kylo', 'Ren', 'kyloren', 'kylo.ren@firstorder.com', '{"theme": "dark", "language": "en"}'),
+ (45, 'Rey', '', 'rey', 'rey@resistance.com', '{"theme": "light", "language": "en"}'),
+ (46, 'Finn', '', 'finn', 'finn@resistance.com', '{"theme": "light", "language": "en"}'),
+ (47, 'Poe', 'Dameron', 'poedameron', 'poe.dameron@resistance.com', '{"theme": "light", "language": "en"}'),
+ (48, 'Arthur', 'Dent', 'arthurdent', 'arthur.dent@hitchhikersguide.com', '{"theme": "light", "language": "en"}'),
+ (49, 'Zaphod', 'Beeblebrox', 'zaphodbeeblebrox', 'zaphod.beeblebrox@hitchhikersguide.com', '{"theme": "dark", "language": "en"}'),
+ (50, 'Ford', 'Prefect', 'fordprefect', 'ford.prefect@hitchhikersguide.com', '{"theme": "light", "language": "en"}'),
+ (51, 'Marvin', 'the Paranoid Android', 'marvinandroid', 'marvin@hitchhikersguide.com', '{"theme": "dark", "language": "en"}'),
+ (52, 'Arthur', 'Morgan', 'arthurmorgan', 'arthur.morgan@reddead.com', '{"theme": "dark", "language": "en"}'),
+ (53, 'John', 'Marston', 'johnmarston', 'john.marston@reddead.com', '{"theme": "dark", "language": "en"}'),
+ (54, 'Tommy', 'Vercetti', 'tommyvercetti', 'tommy.vercetti@vicecity.com', '{"theme": "dark", "language": "en"}'),
+ (55, 'Carl', 'Johnson', 'carljohnson', 'carl.johnson@sweet.com', '{"theme": "dark", "language": "en"}'),
+ (56, 'Lara', 'Croft', 'laracroft', 'lara.croft@tombraider.com', '{"theme": "dark", "language": "en"}'),
+ (57, 'Nathan', 'Drake', 'nathandrake', 'nathan.drake@uncharted.com', '{"theme": "light", "language": "en"}'),
+ (58, 'Kratos', '', 'kratos', 'kratos@godofwar.com', '{"theme": "dark", "language": "en"}'),
+ (59, 'Atreus', '', 'atreus', 'atreus@godofwar.com', '{"theme": "light", "language": "en"}'),
+ (60, 'Geralt', 'of Rivia', 'geraltofrivia', 'geralt@witcher.com', '{"theme": "dark", "language": "en"}'),
+ (61, 'Ciri', '', 'ciri', 'ciri@witcher.com', '{"theme": "light", "language": "en"}'),
+ (62, 'Yennefer', 'of Vengerberg', 'yennefer', 'yennefer@witcher.com', '{"theme": "dark", "language": "en"}'),
+ (63, 'Batman', '', 'batman', 'batman@gotham.com', '{"theme": "dark", "language": "en"}'),
+ (64, 'Superman', '', 'superman', 'superman@metropolis.com', '{"theme": "light", "language": "en"}'),
+ (65, 'Wolverine', '', 'wolverine', 'wolverine@xmen.com', '{"theme": "dark", "language": "en"}'),
+ (66, 'Charles', 'Xavier', 'charlesxavier', 'charles.xavier@xmen.com', '{"theme": "light", "language": "en"}'),
+ (67, 'Logan', '', 'logan', 'logan@xmen.com', '{"theme": "dark", "language": "en"}'),
+ (68, 'Jean', 'Grey', 'jeangrey', 'jean.grey@xmen.com', '{"theme": "light", "language": "en"}'),
+ (69, 'Magneto', '', 'magneto', 'magneto@brotherhood.com', '{"theme": "dark", "language": "en"}'),
+ (70, 'Deadpool', '', 'deadpool', 'deadpool@xmen.com', '{"theme": "dark", "language": "en"}'),
+ (71, 'Thanos', '', 'thanos', 'thanos@titan.com', '{"theme": "dark", "language": "en"}'),
+ (72, 'Rocket', 'Raccoon', 'rocketraccoon', 'rocket.raccoon@guardians.com', '{"theme": "light", "language": "en"}'),
+ (73, 'Groot', '', 'groot', 'groot@guardians.com', '{"theme": "light", "language": "en"}'),
+ (74, 'Gamora', '', 'gamora', 'gamora@guardians.com', '{"theme": "dark", "language": "en"}'),
+ (75, 'Star-Lord', '', 'starlord', 'star-lord@guardians.com', '{"theme": "light", "language": "en"}'),
+ (76, 'Drax', 'the Destroyer', 'drax', 'drax@guardians.com', '{"theme": "dark", "language": "en"}'),
+ (77, 'Sheldon', 'Cooper', 'sheldoncooper', 'sheldon.cooper@caltech.edu', '{"theme": "light", "language": "en"}'),
+ (78, 'Leonard', 'Hofstadter', 'leonardhofstadter', 'leonard.hofstadter@caltech.edu', '{"theme": "light", "language": "en"}'),
+ (79, 'Penny', '', 'penny', 'penny@thecheesecakefactory.com', '{"theme": "light", "language": "en"}'),
+ (80, 'Howard', 'Wolowitz', 'howardwolowitz', 'howard.wolowitz@caltech.edu', '{"theme": "light", "language": "en"}'),
+ (81, 'Raj', 'Koothrappali', 'rajkoothrappali', 'raj.koothrappali@caltech.edu', '{"theme": "light", "language": "en"}'),
+ (82, 'Jesse', 'Pinkman', 'jessepinkman', 'jesse.pinkman@lospolloshermanos.com', '{"theme": "dark", "language": "en"}'),
+ (83, 'Walter', 'White', 'walterwhite', 'walter.white@lospolloshermanos.com', '{"theme": "dark", "language": "en"}'),
+ (84, 'Saul', 'Goodman', 'saulgoodman', 'saul.goodman@law.com', '{"theme": "dark", "language": "en"}'),
+ (85, 'Homer', 'Simpson', 'homersimpson', 'homer.simpson@thesimpsons.com', '{"theme": "light", "language": "en"}'),
+ (86, 'Marge', 'Simpson', 'margesimpson', 'marge.simpson@thesimpsons.com', '{"theme": "light", "language": "en"}'),
+ (87, 'Bart', 'Simpson', 'bartsimpson', 'bart.simpson@thesimpsons.com', '{"theme": "light", "language": "en"}'),
+ (88, 'Lisa', 'Simpson', 'lisasimpson', 'lisa.simpson@thesimpsons.com', '{"theme": "light", "language": "en"}'),
+ (89, 'Maggie', 'Simpson', 'maggiesimpson', 'maggie.simpson@thesimpsons.com', '{"theme": "light", "language": "en"}'),
+ (90, 'Rick', 'Sanchez', 'ricksanchez', 'rick.sanchez@rickandmorty.com', '{"theme": "dark", "language": "en"}'),
+ (91, 'Morty', 'Smith', 'mortysmith', 'morty.smith@rickandmorty.com', '{"theme": "light", "language": "en"}'),
+ (92, 'Jerry', 'Smith', 'jerrysmith', 'jerry.smith@rickandmorty.com', '{"theme": "light", "language": "en"}'),
+ (93, 'Summer', 'Smith', 'summersmith', 'summer.smith@rickandmorty.com', '{"theme": "light", "language": "en"}'),
+ (94, 'Beth', 'Smith', 'bethsmith', 'beth.smith@rickandmorty.com', '{"theme": "light", "language": "en"}'),
+ (95, 'Fry', 'Phillip J.', 'fry', 'fry@planetexpress.com', '{"theme": "light", "language": "en"}'),
+ (96, 'Bender', 'Rodriguez', 'bender', 'bender@planetexpress.com', '{"theme": "dark", "language": "en"}'),
+ (97, 'Leela', 'Turanga', 'leela', 'leela@planetexpress.com', '{"theme": "light", "language": "en"}'),
+ (98, 'Zoidberg', 'John A.', 'zoidberg', 'zoidberg@planetexpress.com', '{"theme": "light", "language": "en"}'),
+ (99, 'Professor', 'Farnsworth', 'farnsworth', 'farnsworth@planetexpress.com', '{"theme": "dark", "language": "en"}'),
+ (100, 'Hermes', 'Conrad', 'hermes', 'hermes@planetexpress.com', '{"theme": "light", "language": "en"}'),
+ (101, 'Amy', 'Wong', 'amy', 'amy@planetexpress.com', '{"theme": "light", "language": "en"}'),
+ (102, 'SpongeBob', 'SquarePants', 'spongebob', 'spongebob@bikinibottom.com', '{"theme": "light", "language": "en"}'),
+ (103, 'Patrick', 'Star', 'patrickstar', 'patrick@bikinibottom.com', '{"theme": "light", "language": "en"}'),
+ (104, 'Squidward', 'Tentacles', 'squidward', 'squidward@bikinibottom.com', '{"theme": "dark", "language": "en"}'),
+ (105, 'Mr.', 'Krabs', 'mrkrabs', 'mr.krabs@bikinibottom.com', '{"theme": "dark", "language": "en"}'),
+ (106, 'Plankton', 'Sheldon J.', 'plankton', 'plankton@bikinibottom.com', '{"theme": "dark", "language": "en"}'),
+ (107, 'Sandy', 'Cheeks', 'sandycheeks', 'sandy.cheeks@bikinibottom.com', '{"theme": "light", "language": "en"}'),
+ (108, 'Michael', 'Scott', 'michaelscott', 'michael.scott@dundermifflin.com', '{"theme": "light", "language": "en"}'),
+ (109, 'Dwight', 'Schrute', 'dwightschrute', 'dwight.schrute@dundermifflin.com', '{"theme": "dark", "language": "en"}'),
+ (110, 'Jim', 'Halpert', 'jimhalpert', 'jim.halpert@dundermifflin.com', '{"theme": "light", "language": "en"}'),
+ (111, 'Pam', 'Beesly', 'pambeesly', 'pam.beesly@dundermifflin.com', '{"theme": "light", "language": "en"}'),
+ (112, 'Stanley', 'Hudson', 'stanleyhudson', 'stanley.hudson@dundermifflin.com', '{"theme": "dark", "language": "en"}'),
+ (113, 'Kevin', 'Malone', 'kevinmalone', 'kevin.malone@dundermifflin.com', '{"theme": "light", "language": "en"}'),
+ (114, 'Oscar', 'Martinez', 'oscarmartinez', 'oscar.martinez@dundermifflin.com', '{"theme": "light", "language": "en"}'),
+ (115, 'Phyllis', 'Vance', 'phyllisvance', 'phyllis.vance@dundermifflin.com', '{"theme": "light", "language": "en"}'),
+ (116, 'Angela', 'Martin', 'angelamartin', 'angela.martin@dundermifflin.com', '{"theme": "dark", "language": "en"}'),
+ (117, 'Andy', 'Bernard', 'andybernard', 'andy.bernard@dundermifflin.com', '{"theme": "light", "language": "en"}'),
+ (118, 'Creed', 'Bratton', 'creedbratton', 'creed.bratton@dundermifflin.com', '{"theme": "dark", "language": "en"}'),
+ (119, 'Meredith', 'Palmer', 'meredithpalmer', 'meredith.palmer@dundermifflin.com', '{"theme": "dark", "language": "en"}'),
+ (120, 'Ryan', 'Howard', 'ryanhoward', 'ryan.howard@dundermifflin.com', '{"theme": "dark", "language": "en"}'),
+ (121, 'Kelly', 'Kapoor', 'kellykapoor', 'kelly.kapoor@dundermifflin.com', '{"theme": "light", "language": "en"}'),
+ (122, 'Toby', 'Flenderson', 'tobyflenderson', 'toby.flenderson@dundermifflin.com', '{"theme": "dark", "language": "en"}'),
+ (123, 'Daryl', 'Philbin', 'darylphilbin', 'daryl.philbin@dundermifflin.com', '{"theme": "light", "language": "en"}');
+
+INSERT INTO identity.Credentials (user_id, provider, provider_id, provider_data, used_last, used_count)
+VALUES (1, 'password', '$2a$10$IphbkzyF2NZlEvvFXrQw5eELqiTV3U.u7Hx4QMCA0yhpubUsUMNnW', '{"algorithm": "bcrypt", "salt": "a8f5f167f44f4964e6c998dee827110c"}', CURRENT_TIMESTAMP, 5),
+ (1, 'twitter', '192837465', '{"id_str": "192837465", "name": "John Doe", "screen_name": "johndoe", "location": "Metropolis, USA", "profile_image_url_https": "https://pbs.twimg.com/profile_images/123456789/johndoe_400x400.jpg", "email": "johndoe@example.com", "verified": true}', CURRENT_TIMESTAMP, 15),
+ (2, 'password', '$2a$10$PEK5WPg5qxKOmntN8sT4bOzOf/omzk0.CVNJAKp2MBdS4o2Mpgq2G', '{"algorithm": "bcrypt", "salt": "7b9c1c8d5b3e469fb6d4e98b9578efc5"}', CURRENT_TIMESTAMP, 4),
+ (3, 'password', '$2a$10$Fot3cix5fRC7HMqXN7jTvuqfsVVCM5Wp5G2RSeVkf1U1NdkfBG6Jy', '{"algorithm": "bcrypt", "salt": "1d6f82c8b3494fda8b183a8e47d66d14"}', CURRENT_TIMESTAMP, 7),
+ (4, 'google', '108947621839247391020', '{"sub": "108947621839247391020", "name": "Bruce Wayne", "given_name": "Bruce", "family_name": "Wayne", "picture": "https://lh3.googleusercontent.com/a-/AOh14Gg-batmanprofilepic/AAAAAAAAAAA/photo.jpg", "email": "bruce.wayne@wayneenterprises.com", "email_verified": true, "locale": "en"}', CURRENT_TIMESTAMP, 12),
+ (5, 'facebook', '10293847561234567', '{"id": "10293847561234567", "name": "Clark Kent", "first_name": "Clark", "last_name": "Kent", "email": "clark.kent@dailyplanet.com", "picture": {"data": {"url": "https://graph.facebook.com/10293847561234567/picture?type=large"}}, "locale": "en_US"}', CURRENT_TIMESTAMP, 3),
+ (6, 'linkedin', 'diana-prince-123456789', '{"id": "diana-prince-123456789", "localizedFirstName": "Diana", "localizedLastName": "Prince", "profilePicture": {"displayImage": "https://media-exp1.licdn.com/dms/image/C4E03AQFZ8DqeOgx5YA/profile-displayphoto-shrink_200_200/0/1517436071201?e=1624320000&v=beta&t=LQyE8g4BLwNxYc6GZJ9Nz4u8MJE1z3kJDcF6Gf9OMwY"}, "emailAddress": "diana.prince@themyscira.gov"}', CURRENT_TIMESTAMP, 6),
+ (7, 'password', '$2a$10$kf2vdK9OY8mnTiT5VQYK2u80PhxTIJmKc1y/9UUphrI2vPUougX/W', '{"algorithm": "bcrypt", "salt": "4c6e2bbad3f74a4cbf50e08f7e9a2951"}', CURRENT_TIMESTAMP, 9),
+ (8, 'twitter', '25073877', '{"id_str": "25073877", "name": "Tony Stark", "screen_name": "IronMan", "location": "Stark Tower, NYC", "profile_image_url_https": "https://pbs.twimg.com/profile_images/875400512/tonystark_400x400.jpg", "email": "tony.stark@starkindustries.com", "verified": true}', CURRENT_TIMESTAMP, 20),
+ (9, 'password', '$2a$10$mXyJ3ERqiZFnDUzWoe/BqOTji0VSRAPHu5CHmQCnXcNvjB3dAagne', '{"algorithm": "bcrypt", "salt": "9f8e7c9d3b414f5e94b4670b8e5a2b19"}', CURRENT_TIMESTAMP, 11),
+ (10, 'password', '$2a$10$l5c6QaTo/dIJOcUCoooWaOJrD7IvKH52PNCcGxONn5rPD5JLa/ZsC', '{"algorithm": "bcrypt", "salt": "e3c0d5f6a1f84f45b6c3e8e17d8c925b"}', CURRENT_TIMESTAMP, 8),
+ (11, 'google', '116834789476231098765', '{"sub": "116834789476231098765", "name": "Bruce Banner", "given_name": "Bruce", "family_name": "Banner", "picture": "https://lh3.googleusercontent.com/a-/AOh14Gg-hulkprofilepic/AAAAAAAAAAA/photo.jpg", "email": "bruce.banner@avengers.com", "email_verified": true, "locale": "en"}', CURRENT_TIMESTAMP, 14),
+ (12, 'password', '$2a$10$kVnyLvCybOAxcB7zF/g.W.628fux.jbNoCN8tfIZnt8Ct9/liMer.', '{"algorithm": "bcrypt", "salt": "b7d9e4c5a6f34d88b6c3f1a7c0e2b839"}', CURRENT_TIMESTAMP, 6),
+ (13, 'facebook', '20394857618234567', '{"id": "20394857618234567", "name": "Harry Potter", "first_name": "Harry", "last_name": "Potter", "email": "harry.potter@hogwarts.ac.uk", "picture": {"data": {"url": "https://graph.facebook.com/20394857618234567/picture?type=large"}}, "locale": "en_GB"}', CURRENT_TIMESTAMP, 2),
+ (14, 'linkedin', 'hermione-granger-234567890', '{"id": "hermione-granger-234567890", "localizedFirstName": "Hermione", "localizedLastName": "Granger", "profilePicture": {"displayImage": "https://media-exp1.licdn.com/dms/image/C4E03AQGxZSDdfx3Y5A/profile-displayphoto-shrink_200_200/0/1517535071201?e=1624320000&v=beta&t=KJ2F8w5IZKx5BsAUbjf8_YD3X9O9s3kFDZpD2aZ3PfM"}, "emailAddress": "hermione.granger@hogwarts.ac.uk"}', CURRENT_TIMESTAMP, 5),
+ (15, 'twitter', '33421598', '{"id_str": "33421598", "name": "Ron Weasley", "screen_name": "TheWeasel", "location": "The Burrow, Ottery St Catchpole", "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000/ronweasley_400x400.jpg", "email": "ron.weasley@hogwarts.ac.uk", "verified": true}', CURRENT_TIMESTAMP, 4),
+ (16, 'password', '$2a$10$pLnLSBoC1Ucrkh9QQzSlQO9BS5oeLqiZR2tymbuoZRED430p7euwG', '{"algorithm": "bcrypt", "salt": "c3a7e2d8f1b74b56b9c8f4d9e6a7c235"}', CURRENT_TIMESTAMP, 10),
+ (17, 'google', '109865432198765432109', '{"sub": "109865432198765432109", "name": "Severus Snape", "given_name": "Severus", "family_name": "Snape", "picture": "https://lh3.googleusercontent.com/a-/AOh14Gg-snapepic/AAAAAAAAAAA/photo.jpg", "email": "severus.snape@hogwarts.ac.uk", "email_verified": true, "locale": "en"}', CURRENT_TIMESTAMP, 13),
+ (18, 'password', '$2a$10$OpNiFmlD2oPCLMSgxvCnDev1ROrahuH4S.nqbvX3VFSnkxWuTHKIe', '{"algorithm": "bcrypt", "salt": "5a6c8f9d2e3b4f17c6e7b9f8d5a2c3e4"}', CURRENT_TIMESTAMP, 6),
+ (19, 'password', '$2a$10$rqge0CSMuq1b2lluLF5EKO7eOP19YX0qn2Ikmd/OKN.sMLIqPCE5u', '{"algorithm": "bcrypt", "salt": "2f3e5d6c7b4a1d8e9c2f7b5e6a4d9c13"}', CURRENT_TIMESTAMP, 7),
+ (20, 'linkedin', 'gandalf-grey-345678901', '{"id": "gandalf-grey-345678901", "localizedFirstName": "Gandalf", "localizedLastName": "the Grey", "profilePicture": {"displayImage": "https://media-exp1.licdn.com/dms/image/C4E03AQFZ8DqeOgx5YA/profile-displayphoto-shrink_200_200/0/1517436071201?e=1624320000&v=beta&t=LQyE8g4BLwNxYc6GZJ9Nz4u8MJE1z3kJDcF6Gf9OMwY"}, "emailAddress": "gandalf@middleearth.me"}', CURRENT_TIMESTAMP, 9),
+ (21, 'facebook', '30495876123456789', '{"id": "30495876123456789", "name": "Aragorn Elessar", "first_name": "Aragorn", "last_name": "Elessar", "email": "aragorn@middleearth.me", "picture": {"data": {"url": "https://graph.facebook.com/30495876123456789/picture?type=large"}}, "locale": "en_US"}', CURRENT_TIMESTAMP, 8),
+ (22, 'password', '$2a$10$r.AQAnbsSTB6VVU2I.pF6u4/qc1Z.rHNUXnxqwxLjNyMIhHnyfuWK', '{"algorithm": "bcrypt", "salt": "3b4d6e7f5a2c1e8f9d7c3b6a4f5e9d18"}', CURRENT_TIMESTAMP, 5),
+ (23, 'password', '$2a$10$Ojml6vqxh.YemFguenicBedeoJN7quOhSrk7839dSR8GGA7rjyuOe', '{"algorithm": "bcrypt", "salt": "1e2f3b4a6d7c5e9f8b3c7d6a4f2e5b19"}', CURRENT_TIMESTAMP, 4);
+
+INSERT INTO identity.PasswordResets (email, token, requested_at, expire_at, used_at)
+VALUES ('johndoe@example.com', 'a1b2c3d4e5f6g7h8i9j0', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP + INTERVAL 1 HOUR, NULL),
+ ('janedoe@example.com', 'f1e2d3c4b5a6g7h8i9j0', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP + INTERVAL 1 HOUR, CURRENT_TIMESTAMP + INTERVAL 10 MINUTE);
+
+INSERT INTO identity.Devices (sid, user_agent)
+VALUES ('550e8400-e29b-41d4-a716-446655440000', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'),
+ ('66eebccb-8d67-4b3e-b218-8a4d24c3f5d3', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15'),
+ ('123e4567-e89b-12d3-a456-426614174000', 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1'),
+ ('92ef1ac7-7391-4b9b-abc7-5f1f0ec9f93b', 'Mozilla/5.0 (Linux; Android 11; SM-G998B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Mobile Safari/537.36'),
+ ('b019fd28-7e8c-4c7d-b432-d3e2b5c74221', 'Mozilla/5.0 (iPad; CPU OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1'),
+ ('f47ac10b-58cc-4372-a567-0e02b2c3d479', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0'),
+ ('21bffb99-78a1-4b92-b162-81cfd8b3bcec', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'),
+ ('77bfcc0d-98d1-4c43-88b2-f3c1e8d3f939', 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1'),
+ ('c9bde7a0-6f79-4a88-b2cb-274abf1d4a62', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36'),
+ ('b43ae61a-4f34-445d-b0d9-6eb8c5e7b13b', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0');
+
+INSERT INTO identity.UserDevices (user_id, device_id, linked_at)
+VALUES (1, 1, CURRENT_TIMESTAMP),
+ (1, 2, CURRENT_TIMESTAMP),
+ (1, 3, CURRENT_TIMESTAMP),
+ (2, 3, CURRENT_TIMESTAMP),
+ (3, 4, CURRENT_TIMESTAMP),
+ (4, 5, CURRENT_TIMESTAMP),
+ (5, 6, CURRENT_TIMESTAMP),
+ (6, 7, CURRENT_TIMESTAMP),
+ (7, 8, CURRENT_TIMESTAMP),
+ (8, 9, CURRENT_TIMESTAMP),
+ (9, 10, CURRENT_TIMESTAMP);
+
+INSERT INTO identity.TrustedDevices (user_id, device_id, name, kind, `usage`, used_last, created_at)
+VALUES (1, 1, 'John\'s Windows PC', 'desktop', 'perso', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
+ (1, 2, 'John\'s MacBook Pro', 'desktop', 'pro', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
+ (3, 4, 'James\' Android Phone', 'phone', 'perso', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
+ (4, 5, 'Bruce\'s iPad', 'tablet', 'pro', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
+
+INSERT INTO identity.AuthLogs (user_id, email, event, ip, ip_location, user_agent, device_id, created_at)
+VALUES (1, 'johndoe@example.com', 'signup', '192.168.1.10', ST_GeomFromText('POINT(48.8588443 2.2943506)'), 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36', 1, CURRENT_TIMESTAMP - INTERVAL 30 DAY),
+ (2, 'janedoe@example.com', 'signup', '172.16.0.15', ST_GeomFromText('POINT(51.507351 -0.127758)'), 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1', 3, CURRENT_TIMESTAMP - INTERVAL 30 DAY),
+ (1, 'johndoe@example.com', 'login_success', '192.168.1.10', NULL, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36', 1, CURRENT_TIMESTAMP - INTERVAL 29 DAY),
+ (2, 'janedoe@example.com', 'login_success', '172.16.0.15', NULL, 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1', 3, CURRENT_TIMESTAMP - INTERVAL 29 DAY),
+ (1, 'johndoe@example.com', 'login_failure', '192.168.1.11', NULL, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15', 2, CURRENT_TIMESTAMP - INTERVAL 20 DAY),
+ (1, 'johndoe@example.com', 'login_success', '192.168.1.11', NULL, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15', 2, CURRENT_TIMESTAMP - INTERVAL 20 DAY),
+ (2, 'janedoe@example.com', 'login_failure', '172.16.0.16', NULL, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36', 1, CURRENT_TIMESTAMP - INTERVAL 20 DAY),
+ (2, 'janedoe@example.com', 'login_success', '172.16.0.16', NULL, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36', 1, CURRENT_TIMESTAMP - INTERVAL 20 DAY),
+ (1, 'johndoe@example.com', 'login_failure', '192.168.1.12', NULL, 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1', 3, CURRENT_TIMESTAMP - INTERVAL 10 DAY),
+ (1, 'johndoe@example.com', 'login_success', '192.168.1.12', NULL, 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1', 3, CURRENT_TIMESTAMP - INTERVAL 10 DAY),
+ (2, 'janedoe@example.com', 'password_reset_asked', '172.16.0.15', NULL, 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1', 3, CURRENT_TIMESTAMP - INTERVAL 10 DAY),
+ (2, 'janedoe@example.com', 'password_reset_used', '172.16.0.15', NULL, 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1', 3, CURRENT_TIMESTAMP - INTERVAL 9 DAY),
+ (1, 'johndoe@example.com', 'password_reset_asked', '192.168.1.10', NULL, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36', 1, CURRENT_TIMESTAMP - INTERVAL 5 DAY),
+ (2, 'janedoe@example.com', 'login_success', '172.16.0.16', NULL, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36', 1, CURRENT_TIMESTAMP - INTERVAL 5 DAY),
+ (1, 'johndoe@example.com', 'password_reset_used', '192.168.1.10', NULL, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36', 1, CURRENT_TIMESTAMP - INTERVAL 4 DAY),
+ (1, 'johndoe@example.com', 'login_success', '192.168.1.10', NULL, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36', 1, CURRENT_TIMESTAMP - INTERVAL 1 DAY),
+ (2, 'janedoe@example.com', 'login_success', '172.16.0.15', NULL, 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1', 3, CURRENT_TIMESTAMP - INTERVAL 1 DAY);
diff --git a/demos/ecommerce/source_03_inventory_oracle.sql b/demos/ecommerce/source_03_inventory_oracle.sql
new file mode 100644
index 000000000..e3197cbed
--- /dev/null
+++ b/demos/ecommerce/source_03_inventory_oracle.sql
@@ -0,0 +1,623 @@
+-- drop everything
+DROP ROLE C##INVENTORY_ROLE;
+DROP USER C##INVENTORY CASCADE;
+
+-- create the database (needs admin rights)
+CREATE USER C##INVENTORY IDENTIFIED BY inventory;
+GRANT UNLIMITED TABLESPACE TO C##INVENTORY;
+CREATE ROLE C##INVENTORY_ROLE;
+GRANT CREATE SESSION, CREATE TABLE, CREATE SEQUENCE, CREATE VIEW, CREATE MATERIALIZED VIEW, CREATE TYPE, CREATE TRIGGER to C##inventory_role;
+GRANT C##INVENTORY_ROLE TO C##INVENTORY;
+ALTER SESSION SET current_schema = C##INVENTORY;
+
+CREATE TABLE C##INVENTORY.EMPLOYEES (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ FIRST_NAME VARCHAR2(255) NOT NULL,
+ LAST_NAME VARCHAR2(255) NOT NULL,
+ EMAIL VARCHAR2(255),
+ PHONE VARCHAR2(20),
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ CREATED_BY NUMBER NOT NULL,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ UPDATED_BY NUMBER NOT NULL,
+ DELETED_AT TIMESTAMP,
+ DELETED_BY NUMBER
+);
+CREATE INDEX IDX_EMPLOYEES_NAME ON C##INVENTORY.EMPLOYEES (FIRST_NAME, LAST_NAME);
+CREATE INDEX IDX_EMPLOYEES_EMAIL ON C##INVENTORY.EMPLOYEES (EMAIL);
+CREATE INDEX IDX_EMPLOYEES_DELETED_AT ON C##INVENTORY.EMPLOYEES (DELETED_AT);
+
+CREATE TABLE C##INVENTORY.WAREHOUSES (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ NAME VARCHAR2(255) UNIQUE,
+ ADDRESS CLOB,
+ MANAGER NUMBER REFERENCES C##INVENTORY.EMPLOYEES (ID),
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_BY NUMBER,
+ DELETED_AT TIMESTAMP,
+ DELETED_BY NUMBER
+);
+CREATE INDEX IDX_WAREHOUSES_DELETED_AT ON C##INVENTORY.WAREHOUSES (DELETED_AT);
+
+CREATE TABLE C##INVENTORY.HALLS (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ WAREHOUSE_ID NUMBER REFERENCES C##INVENTORY.WAREHOUSES (ID),
+ NAME VARCHAR2(255),
+ MANAGER NUMBER REFERENCES C##INVENTORY.EMPLOYEES (ID),
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_BY NUMBER,
+ DELETED_AT TIMESTAMP,
+ DELETED_BY NUMBER
+);
+CREATE INDEX IDX_HALLS_DELETED_AT ON C##INVENTORY.HALLS (DELETED_AT);
+
+CREATE TABLE C##INVENTORY.AISLES (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ HALL_ID NUMBER REFERENCES C##INVENTORY.HALLS (ID),
+ NAME VARCHAR2(255),
+ MANAGER NUMBER REFERENCES C##INVENTORY.EMPLOYEES (ID),
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_BY NUMBER,
+ DELETED_AT TIMESTAMP,
+ DELETED_BY NUMBER
+);
+CREATE INDEX IDX_AISLES_DELETED_AT ON C##INVENTORY.AISLES (DELETED_AT);
+
+CREATE TABLE C##INVENTORY.RACKS (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ AISLE_ID NUMBER REFERENCES C##INVENTORY.AISLES (ID),
+ NAME VARCHAR2(255),
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_BY NUMBER,
+ DELETED_AT TIMESTAMP,
+ DELETED_BY NUMBER
+);
+CREATE INDEX IDX_RACKS_DELETED_AT ON C##INVENTORY.RACKS (DELETED_AT);
+
+CREATE TABLE C##INVENTORY.SHELVES (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ RACK_ID NUMBER REFERENCES C##INVENTORY.RACKS (ID),
+ NAME VARCHAR2(255),
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_BY NUMBER,
+ DELETED_AT TIMESTAMP,
+ DELETED_BY NUMBER
+);
+CREATE INDEX IDX_SHELVES_DELETED_AT ON C##INVENTORY.SHELVES (DELETED_AT);
+
+CREATE TABLE C##INVENTORY.SHELF_POSITIONS (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ SHELF_ID NUMBER REFERENCES C##INVENTORY.SHELVES (ID),
+ NAME VARCHAR2(255),
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_BY NUMBER,
+ DELETED_AT TIMESTAMP,
+ DELETED_BY NUMBER
+);
+CREATE INDEX IDX_SHELF_POSITIONS_DELETED_AT ON C##INVENTORY.SHELF_POSITIONS (DELETED_AT);
+
+CREATE TABLE C##INVENTORY.WAREHOUSE_EMPLOYEES (
+ WAREHOUSE_ID NUMBER REFERENCES C##INVENTORY.WAREHOUSES (ID),
+ EMPLOYEE_ID NUMBER REFERENCES C##INVENTORY.EMPLOYEES (ID),
+ ROLE VARCHAR2(255),
+ "START" TIMESTAMP,
+ END TIMESTAMP,
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_BY NUMBER,
+ DELETED_AT TIMESTAMP,
+ DELETED_BY NUMBER,
+ PRIMARY KEY (WAREHOUSE_ID, EMPLOYEE_ID)
+);
+CREATE INDEX IDX_WAREHOUSE_EMPLOYEES_DELETED_AT ON C##INVENTORY.WAREHOUSE_EMPLOYEES (DELETED_AT);
+
+CREATE TABLE C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS (
+ WAREHOUSE_ID NUMBER REFERENCES C##INVENTORY.WAREHOUSES (ID),
+ EMPLOYEE_ID NUMBER REFERENCES C##INVENTORY.EMPLOYEES (ID),
+ KIND VARCHAR2(255),
+ VALUE VARCHAR2(255),
+ EXPIRE TIMESTAMP,
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ PRIMARY KEY (WAREHOUSE_ID, EMPLOYEE_ID, KIND)
+);
+COMMENT ON TABLE C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS IS 'how to check the employee is identified, can be several';
+
+CREATE TABLE C##INVENTORY.BRANDS (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ SLUG VARCHAR2(255) UNIQUE,
+ NAME VARCHAR2(255) UNIQUE,
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ DELETED_AT TIMESTAMP
+);
+CREATE INDEX IDX_BRANDS_DELETED_AT ON C##INVENTORY.BRANDS (DELETED_AT);
+COMMENT ON COLUMN C##INVENTORY.BRANDS.SLUG IS 'ex: "google"';
+COMMENT ON COLUMN C##INVENTORY.BRANDS.NAME IS 'ex: "Google"';
+
+CREATE TABLE C##INVENTORY.PRODUCTS (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ SLUG VARCHAR2(255) UNIQUE,
+ NAME VARCHAR2(255) UNIQUE,
+ BRAND NUMBER REFERENCES C##INVENTORY.BRANDS (ID),
+ CATEGORY VARCHAR2(255),
+ SUBCATEGORY VARCHAR2(255),
+ WIDTH FLOAT,
+ LENGTH FLOAT,
+ HEIGHT FLOAT,
+ WEIGHT FLOAT,
+ REMARKS CLOB,
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ DELETED_AT TIMESTAMP
+);
+CREATE INDEX IDX_PRODUCTS_DELETED_AT ON C##INVENTORY.PRODUCTS (DELETED_AT);
+COMMENT ON COLUMN C##INVENTORY.PRODUCTS.SLUG IS 'ex: "pixel-8-pro"';
+COMMENT ON COLUMN C##INVENTORY.PRODUCTS.NAME IS 'ex: "Pixel 8 Pro"';
+COMMENT ON COLUMN C##INVENTORY.PRODUCTS.CATEGORY IS 'ex: "Phones"';
+COMMENT ON COLUMN C##INVENTORY.PRODUCTS.SUBCATEGORY IS 'ex: "Smartphones"';
+COMMENT ON COLUMN C##INVENTORY.PRODUCTS.WIDTH IS 'typical width of the product in millis, see PRODUCT_VERSIONS for the real one';
+COMMENT ON COLUMN C##INVENTORY.PRODUCTS.LENGTH IS 'typical length of the product in millis, see PRODUCT_VERSIONS for the real one';
+COMMENT ON COLUMN C##INVENTORY.PRODUCTS.HEIGHT IS 'typical height of the product in millis, see PRODUCT_VERSIONS for the real one';
+COMMENT ON COLUMN C##INVENTORY.PRODUCTS.WEIGHT IS 'typical weight of the product in grams, see PRODUCT_VERSIONS for the real one';
+COMMENT ON COLUMN C##INVENTORY.PRODUCTS.REMARKS IS 'ex: fragile';
+
+CREATE TABLE C##INVENTORY.PRODUCT_VERSIONS (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ PRODUCT_ID NUMBER REFERENCES C##INVENTORY.PRODUCTS (ID),
+ SKU VARCHAR2(12) UNIQUE,
+ EAN VARCHAR2(13) UNIQUE,
+ NAME VARCHAR2(255) UNIQUE,
+ SPECS JSON,
+ WIDTH FLOAT,
+ LENGTH FLOAT,
+ HEIGHT FLOAT,
+ WEIGHT FLOAT,
+ REMARKS CLOB,
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ DELETED_AT TIMESTAMP
+);
+CREATE INDEX IDX_PRODUCT_VERSIONS_DELETED_AT ON C##INVENTORY.PRODUCT_VERSIONS (DELETED_AT);
+COMMENT ON COLUMN C##INVENTORY.PRODUCT_VERSIONS.SKU IS 'internal id';
+COMMENT ON COLUMN C##INVENTORY.PRODUCT_VERSIONS.EAN IS 'european id';
+COMMENT ON COLUMN C##INVENTORY.PRODUCT_VERSIONS.NAME IS 'ex: "Pixel 8 Pro Menthe 128 Go"';
+COMMENT ON COLUMN C##INVENTORY.PRODUCT_VERSIONS.SPECS IS 'specificities of this version, ex: `{color: "Menthe", storage: 128}`';
+COMMENT ON COLUMN C##INVENTORY.PRODUCT_VERSIONS.WIDTH IS 'in millis';
+COMMENT ON COLUMN C##INVENTORY.PRODUCT_VERSIONS.LENGTH IS 'in millis';
+COMMENT ON COLUMN C##INVENTORY.PRODUCT_VERSIONS.HEIGHT IS 'in millis';
+COMMENT ON COLUMN C##INVENTORY.PRODUCT_VERSIONS.WEIGHT IS 'in grams';
+
+CREATE TABLE C##INVENTORY.PHYSICAL_PRODUCTS (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ PRODUCT_VERSION_ID NUMBER REFERENCES C##INVENTORY.PRODUCT_VERSIONS (ID),
+ SNID VARCHAR2(12) UNIQUE,
+ EXPIRATION TIMESTAMP,
+ REMARKS CLOB,
+ STORED NUMBER REFERENCES C##INVENTORY.SHELF_POSITIONS (ID),
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ DELETED_AT TIMESTAMP
+);
+CREATE INDEX IDX_PHYSICAL_PRODUCTS_DELETED_AT ON C##INVENTORY.PHYSICAL_PRODUCTS (DELETED_AT);
+COMMENT ON COLUMN C##INVENTORY.PHYSICAL_PRODUCTS.SNID IS 'serial number of this product';
+COMMENT ON COLUMN C##INVENTORY.PHYSICAL_PRODUCTS.EXPIRATION IS 'when Product has an expiration date, null otherwise';
+
+CREATE TABLE C##INVENTORY.SUPPLIERS (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ NAME VARCHAR2(255),
+ "LEVEL" NUMBER,
+ CURRENCY VARCHAR2(3) CHECK (CURRENCY IN ('USD', 'EUR')),
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ DELETED_AT TIMESTAMP
+);
+CREATE INDEX IDX_SUPPLIERS_NAME ON C##INVENTORY.SUPPLIERS (NAME);
+CREATE INDEX IDX_SUPPLIERS_DELETED_AT ON C##INVENTORY.SUPPLIERS (DELETED_AT);
+COMMENT ON COLUMN C##INVENTORY.SUPPLIERS."LEVEL" IS 'the lower, the more priority is given to this supplier';
+
+CREATE TABLE C##INVENTORY.SUPPLIER_PRICES (
+ SUPPLIER_ID NUMBER REFERENCES C##INVENTORY.SUPPLIERS (ID),
+ PRODUCT_VERSION_ID NUMBER REFERENCES C##INVENTORY.PRODUCT_VERSIONS (ID),
+ PRICE FLOAT,
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ DELETED_AT TIMESTAMP,
+ PRIMARY KEY (SUPPLIER_ID, PRODUCT_VERSION_ID)
+);
+CREATE INDEX IDX_SUPPLIER_PRICES_DELETED_AT ON C##INVENTORY.SUPPLIER_PRICES (DELETED_AT);
+
+CREATE TABLE C##INVENTORY.SUPPLIER_EMPLOYEES (
+ SUPPLIER_ID NUMBER REFERENCES C##INVENTORY.SUPPLIERS (ID),
+ EMPLOYEE_ID NUMBER REFERENCES C##INVENTORY.EMPLOYEES (ID),
+ ROLE VARCHAR2(255),
+ "START" TIMESTAMP,
+ END TIMESTAMP,
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_BY NUMBER,
+ DELETED_AT TIMESTAMP,
+ DELETED_BY NUMBER,
+ PRIMARY KEY (SUPPLIER_ID, EMPLOYEE_ID)
+);
+CREATE INDEX IDX_SUPPLIER_EMPLOYEES_DELETED_AT ON C##INVENTORY.SUPPLIER_EMPLOYEES (DELETED_AT);
+
+CREATE TABLE C##INVENTORY.PURCHASE_ORDERS (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ SUPPLIER_ID NUMBER REFERENCES C##INVENTORY.SUPPLIERS (ID),
+ PRICE FLOAT,
+ CURRENCY VARCHAR2(3) CHECK (CURRENCY IN ('USD', 'EUR')),
+ DETAILS CLOB,
+ NOTES CLOB,
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_BY NUMBER,
+ SENT_AT TIMESTAMP,
+ SENT_BY NUMBER,
+ PAID_AT TIMESTAMP,
+ DELIVERED_AT TIMESTAMP,
+ VALIDATED_AT TIMESTAMP,
+ VALIDATED_BY NUMBER
+);
+COMMENT ON COLUMN C##INVENTORY.PURCHASE_ORDERS.PRICE IS 'total price, computed from items price x quantity';
+COMMENT ON COLUMN C##INVENTORY.PURCHASE_ORDERS.DETAILS IS 'additional text for the supplier';
+COMMENT ON COLUMN C##INVENTORY.PURCHASE_ORDERS.NOTES IS 'internal text for employees';
+COMMENT ON COLUMN C##INVENTORY.PURCHASE_ORDERS.SENT_AT IS 'can''t be updated once sent';
+
+CREATE TABLE C##INVENTORY.PURCHASE_ORDER_ITEMS (
+ PURCHASE_ORDER_ID NUMBER REFERENCES C##INVENTORY.PURCHASE_ORDERS (ID),
+ PRODUCT_VERSION_ID NUMBER REFERENCES C##INVENTORY.PRODUCT_VERSIONS (ID),
+ QUANTITY NUMBER,
+ PRICE FLOAT,
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_BY NUMBER,
+ PRIMARY KEY (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID)
+);
+
+CREATE TABLE C##INVENTORY.DELIVERIES (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ REASON VARCHAR2(255) NOT NULL,
+ ACCEPTED CHAR(1) NOT NULL CHECK (ACCEPTED IN ('Y', 'N')),
+ PURCHASE_ORDER_ID NUMBER REFERENCES C##INVENTORY.PURCHASE_ORDERS (ID),
+ WAREHOUSE_ID NUMBER NOT NULL,
+ WAREHOUSE_EMPLOYEE_ID NUMBER NOT NULL,
+ SUPPLIER_ID NUMBER,
+ SUPPLIER_EMPLOYEE_ID NUMBER,
+ DELIVERED_AT TIMESTAMP,
+ FOREIGN KEY (WAREHOUSE_ID, WAREHOUSE_EMPLOYEE_ID) REFERENCES C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID),
+ FOREIGN KEY (SUPPLIER_ID, SUPPLIER_EMPLOYEE_ID) REFERENCES C##INVENTORY.SUPPLIER_EMPLOYEES (SUPPLIER_ID, EMPLOYEE_ID)
+);
+
+CREATE TABLE C##INVENTORY.DELIVERY_ITEMS (
+ DELIVERY_ID NUMBER REFERENCES C##INVENTORY.DELIVERIES (ID),
+ PHYSICAL_PRODUCT_ID NUMBER REFERENCES C##INVENTORY.PHYSICAL_PRODUCTS (ID),
+ PRIMARY KEY (DELIVERY_ID, PHYSICAL_PRODUCT_ID)
+);
+
+CREATE TABLE C##INVENTORY.PICKUPS (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ REASON VARCHAR2(255),
+ ACCEPTED CHAR(1) CHECK (ACCEPTED IN ('Y', 'N')),
+ WAREHOUSE_ID NUMBER,
+ WAREHOUSE_EMPLOYEE_ID NUMBER,
+ SUPPLIER_ID NUMBER,
+ SUPPLIER_EMPLOYEE_ID NUMBER,
+ DELIVERED_AT TIMESTAMP,
+ FOREIGN KEY (WAREHOUSE_ID, WAREHOUSE_EMPLOYEE_ID) REFERENCES C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID),
+ FOREIGN KEY (SUPPLIER_ID, SUPPLIER_EMPLOYEE_ID) REFERENCES C##INVENTORY.SUPPLIER_EMPLOYEES (SUPPLIER_ID, EMPLOYEE_ID)
+);
+
+CREATE TABLE C##INVENTORY.PICKUP_ITEMS (
+ PICKUP_ID NUMBER REFERENCES C##INVENTORY.PICKUPS (ID),
+ PHYSICAL_PRODUCT_ID NUMBER REFERENCES C##INVENTORY.PHYSICAL_PRODUCTS (ID),
+ PRIMARY KEY (PICKUP_ID, PHYSICAL_PRODUCT_ID)
+);
+
+CREATE TABLE C##INVENTORY.INVENTORIES (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ NAME VARCHAR2(255),
+ WAREHOUSE_ID NUMBER REFERENCES C##INVENTORY.WAREHOUSES (ID),
+ HALL_ID NUMBER REFERENCES C##INVENTORY.HALLS (ID),
+ AISLE_ID NUMBER REFERENCES C##INVENTORY.AISLES (ID),
+ PLANNED TIMESTAMP,
+ FINISHED TIMESTAMP,
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_BY NUMBER
+);
+
+CREATE TABLE C##INVENTORY.INVENTORY_MEMBERS (
+ INVENTORY_ID NUMBER REFERENCES C##INVENTORY.INVENTORIES (ID),
+ WAREHOUSE_ID NUMBER,
+ EMPLOYEE_ID NUMBER,
+ PRIMARY KEY (INVENTORY_ID, WAREHOUSE_ID, EMPLOYEE_ID),
+ FOREIGN KEY (WAREHOUSE_ID, EMPLOYEE_ID) REFERENCES C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID)
+);
+
+CREATE TABLE C##INVENTORY.INVENTORY_OBSERVATIONS (
+ ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ INVENTORY_ID NUMBER REFERENCES C##INVENTORY.INVENTORIES (ID),
+ PHYSICAL_PRODUCT_ID NUMBER REFERENCES C##INVENTORY.PHYSICAL_PRODUCTS (ID),
+ STATUS VARCHAR2(255),
+ MESSAGE CLOB,
+ CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CREATED_BY NUMBER,
+ UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UPDATED_BY NUMBER,
+ DELETED_AT TIMESTAMP,
+ DELETED_BY NUMBER
+);
+CREATE INDEX IDX_INVENTORY_OBSERVATIONS_DELETED_AT ON C##INVENTORY.INVENTORY_OBSERVATIONS (DELETED_AT);
+
+
+-- insert some data
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (1, 'Harry', 'Potter', 'harry.potter@hogwarts.ac.uk', '+1-202-555-0173', 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (2, 'Hermione', 'Granger', 'hermione.granger@hogwarts.ac.uk', '+44-20-7946-0958', 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (3, 'Ron', 'Weasley', 'ron.weasley@hogwarts.ac.uk', '+33-1-45-67-89-01', 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (4, 'Albus', 'Dumbledore', 'albus.dumbledore@hogwarts.ac.uk', '+49-30-1234567', 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (5, 'Severus', 'Snape', 'severus.snape@hogwarts.ac.uk', '+61-2-9374-4000', 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (6, 'Frodo', 'Baggins', 'frodo.baggins@shire.me', NULL, 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (7, 'Samwise', 'Gamgee', 'samwise.gamgee@shire.me', NULL, 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (8, 'Gandalf', 'the Grey', 'gandalf@middleearth.me', NULL, 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (9, 'Aragorn', 'Elessar', 'aragorn@middleearth.me', NULL, 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (10, 'Legolas', 'Greenleaf', 'legolas@woodlandrealm.me', NULL, 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (11, 'Gimli', 'Son of Glóin', 'gimli@lonelymountain.me', NULL, 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (12, 'Steve', 'Rogers', 'steve.rogers@avengers.com', NULL, 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (13, 'Bruce', 'Banner', 'bruce.banner@avengers.com', NULL, 3, 3);
+INSERT INTO C##INVENTORY.EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE, CREATED_BY, UPDATED_BY) VALUES (14, 'Natasha', 'Romanoff', 'natasha.romanoff@shield.gov', NULL, 3, 3);
+
+INSERT INTO C##INVENTORY.WAREHOUSES (NAME, ADDRESS, MANAGER, CREATED_BY, UPDATED_BY) VALUES ('Hogwarts Central Warehouse', 'Hogwarts School of Witchcraft and Wizardry, Highlands, Scotland', 4, 16, 16);
+INSERT INTO C##INVENTORY.WAREHOUSES (NAME, ADDRESS, MANAGER, CREATED_BY, UPDATED_BY) VALUES ('Shire Storage Facility', 'The Shire, Westfarthing, Middle-earth', 8, 20, 20);
+
+INSERT INTO C##INVENTORY.HALLS (WAREHOUSE_ID, NAME, MANAGER, CREATED_BY, UPDATED_BY) VALUES (1, 'Main Hall', 4, 16, 16);
+INSERT INTO C##INVENTORY.HALLS (WAREHOUSE_ID, NAME, MANAGER, CREATED_BY, UPDATED_BY) VALUES (1, 'Potion Storage', 5, 16, 16);
+
+INSERT INTO C##INVENTORY.AISLES (HALL_ID, NAME, MANAGER, CREATED_BY, UPDATED_BY) VALUES (1, 'Wand Aisle', 2, 16, 16);
+INSERT INTO C##INVENTORY.AISLES (HALL_ID, NAME, MANAGER, CREATED_BY, UPDATED_BY) VALUES (1, 'Book Aisle', 2, 16, 16);
+INSERT INTO C##INVENTORY.AISLES (HALL_ID, NAME, MANAGER, CREATED_BY, UPDATED_BY) VALUES (2, 'Ingredients Aisle', 3, 16, 16);
+INSERT INTO C##INVENTORY.AISLES (HALL_ID, NAME, MANAGER, CREATED_BY, UPDATED_BY) VALUES (2, 'Cauldron Aisle', 3, 16, 16);
+
+INSERT INTO C##INVENTORY.RACKS (AISLE_ID, NAME, CREATED_BY, UPDATED_BY) VALUES (1, 'Phoenix Feather Rack', 16, 16);
+INSERT INTO C##INVENTORY.RACKS (AISLE_ID, NAME, CREATED_BY, UPDATED_BY) VALUES (1, 'Dragon Heartstring Rack', 16, 16);
+INSERT INTO C##INVENTORY.RACKS (AISLE_ID, NAME, CREATED_BY, UPDATED_BY) VALUES (2, 'Ancient Texts Rack', 16, 16);
+INSERT INTO C##INVENTORY.RACKS (AISLE_ID, NAME, CREATED_BY, UPDATED_BY) VALUES (2, 'Modern Spellbooks Rack', 16, 16);
+INSERT INTO C##INVENTORY.RACKS (AISLE_ID, NAME, CREATED_BY, UPDATED_BY) VALUES (3, 'Herbs Rack', 16, 16);
+INSERT INTO C##INVENTORY.RACKS (AISLE_ID, NAME, CREATED_BY, UPDATED_BY) VALUES (3, 'Potions Ingredients Rack', 16, 16);
+INSERT INTO C##INVENTORY.RACKS (AISLE_ID, NAME, CREATED_BY, UPDATED_BY) VALUES (4, 'Small Cauldrons Rack', 16, 16);
+INSERT INTO C##INVENTORY.RACKS (AISLE_ID, NAME, CREATED_BY, UPDATED_BY) VALUES (4, 'Large Cauldrons Rack', 16, 16);
+
+BEGIN
+ FOR rack IN (SELECT ID FROM C##INVENTORY.RACKS) LOOP
+ FOR i IN 1..3 LOOP
+ DECLARE
+ v_shelf_id NUMBER;
+ BEGIN
+ INSERT INTO C##INVENTORY.SHELVES (RACK_ID, NAME, CREATED_BY, UPDATED_BY)
+ VALUES (rack.ID, CASE WHEN i = 1 THEN 'Top Shelf' WHEN i = 2 THEN 'Middle Shelf' ELSE 'Bottom Shelf' END, 16, 16)
+ RETURNING ID INTO v_shelf_id;
+
+ FOR j IN 1..3 LOOP
+ INSERT INTO C##INVENTORY.SHELF_POSITIONS (SHELF_ID, NAME, CREATED_BY, UPDATED_BY)
+ VALUES (v_shelf_id, CASE WHEN j = 1 THEN 'Position A' WHEN j = 2 THEN 'Position B' ELSE 'Position C' END, 16, 16);
+ END LOOP;
+ END;
+ END LOOP;
+ END LOOP;
+END;
+
+INSERT INTO C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (1, 1, 'stocker', 3, 3);
+INSERT INTO C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (1, 2, 'stocker', 3, 3);
+INSERT INTO C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (1, 3, 'loader', 3, 3);
+INSERT INTO C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (1, 4, 'manager', 3, 3);
+INSERT INTO C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (1, 5, 'receiver', 3, 3);
+INSERT INTO C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (2, 6, 'stocker', 3, 3);
+INSERT INTO C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (2, 7, 'stocker', 3, 3);
+INSERT INTO C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (2, 8, 'manager', 3, 3);
+INSERT INTO C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (2, 9, 'receiver', 3, 3);
+INSERT INTO C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (2, 10, 'loader', 3, 3);
+INSERT INTO C##INVENTORY.WAREHOUSE_EMPLOYEES (WAREHOUSE_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (2, 11, 'loader', 3, 3);
+
+INSERT INTO C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS (WAREHOUSE_ID, EMPLOYEE_ID, KIND, VALUE, EXPIRE, CREATED_BY) VALUES (1, 1, 'name', 'Harry Potter', NULL, 16);
+INSERT INTO C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS (WAREHOUSE_ID, EMPLOYEE_ID, KIND, VALUE, EXPIRE, CREATED_BY) VALUES (1, 1, 'badge', 'HP1234', NULL, 16);
+INSERT INTO C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS (WAREHOUSE_ID, EMPLOYEE_ID, KIND, VALUE, EXPIRE, CREATED_BY) VALUES (1, 1, 'cni', '01 23 45 678 901', NULL, 16);
+INSERT INTO C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS (WAREHOUSE_ID, EMPLOYEE_ID, KIND, VALUE, EXPIRE, CREATED_BY) VALUES (1, 2, 'badge', 'HG5678', NULL, 16);
+INSERT INTO C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS (WAREHOUSE_ID, EMPLOYEE_ID, KIND, VALUE, EXPIRE, CREATED_BY) VALUES (1, 2, 'cni', '02 34 56 789 012', NULL, 16);
+INSERT INTO C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS (WAREHOUSE_ID, EMPLOYEE_ID, KIND, VALUE, EXPIRE, CREATED_BY) VALUES (1, 3, 'badge', 'RW9101', NULL, 16);
+INSERT INTO C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS (WAREHOUSE_ID, EMPLOYEE_ID, KIND, VALUE, EXPIRE, CREATED_BY) VALUES (1, 3, 'cni', '03 45 67 890 123', NULL, 16);
+INSERT INTO C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS (WAREHOUSE_ID, EMPLOYEE_ID, KIND, VALUE, EXPIRE, CREATED_BY) VALUES (1, 4, 'badge', 'AD1213', NULL, 16);
+INSERT INTO C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS (WAREHOUSE_ID, EMPLOYEE_ID, KIND, VALUE, EXPIRE, CREATED_BY) VALUES (1, 4, 'cni', '04 56 78 901 234', NULL, 16);
+INSERT INTO C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS (WAREHOUSE_ID, EMPLOYEE_ID, KIND, VALUE, EXPIRE, CREATED_BY) VALUES (1, 5, 'badge', 'SS1415', NULL, 16);
+INSERT INTO C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS (WAREHOUSE_ID, EMPLOYEE_ID, KIND, VALUE, EXPIRE, CREATED_BY) VALUES (1, 5, 'cni', '05 67 89 012 345', NULL, 16);
+
+INSERT INTO C##INVENTORY.BRANDS (SLUG, NAME) VALUES ('google', 'Google');
+INSERT INTO C##INVENTORY.BRANDS (SLUG, NAME) VALUES ('apple', 'Apple');
+
+INSERT INTO C##INVENTORY.PRODUCTS (ID, SLUG, NAME, BRAND, CATEGORY, SUBCATEGORY, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (1, 'pixel-8', 'Pixel 8', 1, 'Phones', 'Smartphones', 150.5, 70.8, 8.9, 187);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (1, 1, 'GGLPX8O128', '0840244706692', 'Pixel 8 Obsidian 128 Go', '{"color": "Obsidian", "storage": 128}', 150.5, 70.8, 8.9, 187);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (2, 1, 'GGLPX8O256', '0840244706906', 'Pixel 8 Obsidian 256 Go', '{"color": "Obsidian", "storage": 256}', 150.5, 70.8, 8.9, 187);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (3, 1, 'GGLPX8H128', '0840244706807', 'Pixel 8 Hazel 128 Go', '{"color": "Hazel", "storage": 128}', 150.5, 70.8, 8.9, 187);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (4, 1, 'GGLPX8H256', '0840244707118', 'Pixel 8 Hazel 256 Go', '{"color": "Hazel", "storage": 256}', 150.5, 70.8, 8.9, 187);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (5, 1, 'GGLPX8R128', '0840244706982', 'Pixel 8 Rose 128 Go', '{"color": "Rose", "storage": 128}', 150.5, 70.8, 8.9, 187);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (6, 1, 'GGLPX8R256', '0840244707231', 'Pixel 8 Rose 256 Go', '{"color": "Rose", "storage": 256}', 150.5, 70.8, 8.9, 187);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (7, 1, 'GGLPX8M128', '0840244707071', 'Pixel 8 Mint 128 Go', '{"color": "Mint", "storage": 128}', 150.5, 70.8, 8.9, 187);
+INSERT INTO C##INVENTORY.PRODUCTS (ID, SLUG, NAME, BRAND, CATEGORY, SUBCATEGORY, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (2, 'pixel-8-pro', 'Pixel 8 Pro', 1, 'Phones', 'Smartphones', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (8, 2, 'GGLPX8PO128', '0840244705046', 'Pixel 8 Pro Obsidian 128 Go', '{"color": "Obsidian", "storage": 128}', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (9, 2, 'GGLPX8PO256', '0840244705299', 'Pixel 8 Pro Obsidian 256 Go', '{"color": "Obsidian", "storage": 256}', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (10, 2, 'GGLPX8PO512', '0840244705565', 'Pixel 8 Pro Obsidian 512 Go', '{"color": "Obsidian", "storage": 512}', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (11, 2, 'GGLPX8PB128', '0840244705206', 'Pixel 8 Pro Bay 128 Go', '{"color": "Bay", "storage": 128}', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (12, 2, 'GGLPX8PB256', '0840244705473', 'Pixel 8 Pro Bay 256 Go', '{"color": "Bay", "storage": 256}', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (13, 2, 'GGLPX8PP128', '0840244705121', 'Pixel 8 Pro Porcelain 128 Go', '{"color": "Porcelain", "storage": 128}', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (14, 2, 'GGLPX8PP256', '0840244705381', 'Pixel 8 Pro Porcelain 256 Go', '{"color": "Porcelain", "storage": 256}', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCTS (ID, SLUG, NAME, BRAND, CATEGORY, SUBCATEGORY, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (3, 'pixel-8a', 'Pixel 8a', 1, 'Phones', 'Smartphones', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCTS (ID, SLUG, NAME, BRAND, CATEGORY, SUBCATEGORY, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (4, 'pixel-9', 'Pixel 9', 1, 'Phones', 'Smartphones', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCTS (ID, SLUG, NAME, BRAND, CATEGORY, SUBCATEGORY, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (6, 'pixel-9-pro', 'Pixel 9 Pro', 1, 'Phones', 'Smartphones', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCTS (ID, SLUG, NAME, BRAND, CATEGORY, SUBCATEGORY, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (5, 'pixel-9-pro-xl', 'Pixel 9 Pro XL', 1, 'Phones', 'Smartphones', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCTS (ID, SLUG, NAME, BRAND, CATEGORY, SUBCATEGORY, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (7, 'pixel-9-pro-fold', 'Pixel 9 Pro Fold', 1, 'Phones', 'Smartphones', 162.6, 76.5, 8.8, 213);
+INSERT INTO C##INVENTORY.PRODUCTS (ID, SLUG, NAME, BRAND, CATEGORY, SUBCATEGORY, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (8, 'pixel-8-case', 'Pixel 8 Case', 1, 'Phones', 'Accessories', 154.8, 75.1, 13.4, 27.3);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (15, 8, 'GGLPX8CH', '0840244703806', 'Pixel 8 Case Hazel', '{"color": "Hazel"}', 154.8, 75.1, 13.4, 27.3);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (16, 8, 'GGLPX8CC', '0840244703807', 'Pixel 8 Case Coral', '{"color": "Coral"}', 154.8, 75.1, 13.4, 27.3);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (17, 8, 'GGLPX8CM', '0840244703783', 'Pixel 8 Case Mint', '{"color": "Mint"}', 154.8, 75.1, 13.4, 27.3);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (18, 8, 'GGLPX8CR', '0840244703813', 'Pixel 8 Case Rose', '{"color": "Rose"}', 154.8, 75.1, 13.4, 27.3);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (19, 8, 'GGLPX8CCH', '0840244703790', 'Pixel 8 Case Charcoal', '{"color": "Charcoal"}', 154.8, 75.1, 13.4, 27.3);
+INSERT INTO C##INVENTORY.PRODUCTS (ID, SLUG, NAME, BRAND, CATEGORY, SUBCATEGORY, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (9, 'pixel-8-casemate-signature', 'Pixel 8 Case Signature', 1, 'Phones', 'Accessories', 155.7, 76.5, 13.7, 1.7);
+INSERT INTO C##INVENTORY.PRODUCT_VERSIONS (ID, PRODUCT_ID, SKU, EAN, NAME, SPECS, WIDTH, LENGTH, HEIGHT, WEIGHT) VALUES (20, 9, 'GGLPX8CS', '0840244703900', 'Pixel 8 Case Signature Clear', '{}', 155.7, 76.5, 13.7, 1.7);
+
+INSERT INTO C##INVENTORY.SUPPLIERS (NAME, "LEVEL", CURRENCY) VALUES ('Google', 1, 'USD');
+INSERT INTO C##INVENTORY.SUPPLIERS (NAME, "LEVEL", CURRENCY) VALUES ('FNAC', 2, 'EUR');
+
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 1, 699);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 2, 759);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 3, 699);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 4, 759);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 5, 699);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 6, 759);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 7, 699);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 8, 999);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 9, 1059);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 10, 1179);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 11, 999);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 12, 1059);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 13, 999);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 14, 1059);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 15, 34.99);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 16, 34.99);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 17, 34.99);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 18, 34.99);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 19, 34.99);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (1, 20, 29.99);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 1, 549);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 2, 609);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 3, 549);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 4, 609);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 5, 549);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 6, 609);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 7, 549);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 8, 749);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 9, 809);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 10, 949);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 11, 749);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 12, 809);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 13, 749);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 14, 809);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 15, 39.99);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 17, 39.99);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 18, 39.99);
+INSERT INTO C##INVENTORY.SUPPLIER_PRICES (SUPPLIER_ID, PRODUCT_VERSION_ID, PRICE) VALUES (2, 19, 39.99);
+
+INSERT INTO C##INVENTORY.SUPPLIER_EMPLOYEES (SUPPLIER_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (1, 12, 'sale', 3, 3);
+INSERT INTO C##INVENTORY.SUPPLIER_EMPLOYEES (SUPPLIER_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (1, 13, 'delivery', 3, 3);
+INSERT INTO C##INVENTORY.SUPPLIER_EMPLOYEES (SUPPLIER_ID, EMPLOYEE_ID, ROLE, CREATED_BY, UPDATED_BY) VALUES (2, 14, 'delivery', 3, 3);
+
+INSERT INTO C##INVENTORY.PURCHASE_ORDERS (SUPPLIER_ID, PRICE, CURRENCY, DETAILS, NOTES, CREATED_BY, UPDATED_BY) VALUES (1, 9471.92, 'USD', 'Order 1 from Google', 'Make initial stock', 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (1, 1, 3, 699, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (1, 2, 2, 759, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (1, 3, 2, 699, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (1, 5, 2, 699, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (1, 7, 2, 699, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (1, 15, 3, 34.99, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (1, 16, 2, 34.99, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (1, 20, 3, 29.99, 16, 16);
+
+INSERT INTO C##INVENTORY.PURCHASE_ORDERS (SUPPLIER_ID, PRICE, CURRENCY, DETAILS, NOTES, CREATED_BY, UPDATED_BY) VALUES (2, 7121, 'EUR', 'Order 1 from FNAC', 'Make initial stock', 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (2, 8, 3, 749, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (2, 9, 1, 809, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (2, 10, 1, 949, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (2, 11, 1, 749, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (2, 12, 1, 809, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (2, 13, 1, 749, 16, 16);
+INSERT INTO C##INVENTORY.PURCHASE_ORDER_ITEMS (PURCHASE_ORDER_ID, PRODUCT_VERSION_ID, QUANTITY, PRICE, CREATED_BY, UPDATED_BY) VALUES (2, 14, 1, 809, 16, 16);
+
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (1, 1, 'F2ST123456', NULL, NULL, NULL);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (2, 1, 'F2ST123457', NULL, NULL, 1);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (3, 1, 'F2ST123458', NULL, NULL, 1);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (4, 2, 'F2ST123459', NULL, NULL, NULL);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (5, 2, 'F2ST123460', NULL, NULL, 2);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (6, 3, 'FK3B789012', NULL, NULL, 2);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (7, 3, 'FK3B789013', NULL, NULL, 2);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (8, 5, 'FK3B789014', NULL, NULL, 3);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (9, 5, 'FK3B789015', NULL, NULL, 3);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (10, 7, 'FK3B789016', NULL, NULL, 3);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (11, 7, 'FG8N456789', NULL, NULL, 3);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (12, 15, 'FG8N456790', NULL, NULL, NULL);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (13, 15, 'FG8N456791', NULL, NULL, 28);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (14, 15, 'FG8N456792', NULL, NULL, 28);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (15, 16, 'FG8N456793', NULL, NULL, 29);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (16, 16, 'FL5H345678', NULL, NULL, 29);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (17, 20, 'FL5H345679', NULL, NULL, NULL);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (18, 20, 'FL5H345680', NULL, NULL, 30);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (19, 20, 'FL5H345681', NULL, NULL, 30);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (20, 8, 'FM7J234567', NULL, NULL, 10);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (21, 8, 'FM7J234568', NULL, NULL, 10);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (22, 8, 'FM7J234569', NULL, NULL, 10);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (23, 9, 'FM7J234570', NULL, NULL, 11);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (24, 10, 'FM7J234571', NULL, NULL, 11);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (25, 11, 'FM7J234572', NULL, NULL, 11);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (26, 12, 'FM7J234573', NULL, NULL, 12);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (27, 13, 'FM7J234574', NULL, NULL, 12);
+INSERT INTO C##INVENTORY.PHYSICAL_PRODUCTS (ID, PRODUCT_VERSION_ID, SNID, EXPIRATION, REMARKS, STORED) VALUES (28, 14, 'FM7J234575', NULL, NULL, 12);
+
+INSERT INTO C##INVENTORY.DELIVERIES (REASON, ACCEPTED, PURCHASE_ORDER_ID, WAREHOUSE_ID, WAREHOUSE_EMPLOYEE_ID, SUPPLIER_ID, SUPPLIER_EMPLOYEE_ID, DELIVERED_AT) VALUES ('supplier_delivery', 'Y', 1, 1, 4, 1, 13, CURRENT_TIMESTAMP);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 1);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 2);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 3);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 4);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 5);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 6);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 7);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 8);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 9);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 10);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 11);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 12);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 13);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 14);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 15);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 16);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 17);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 18);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 19);
+
+INSERT INTO C##INVENTORY.DELIVERIES (REASON, ACCEPTED, PURCHASE_ORDER_ID, WAREHOUSE_ID, WAREHOUSE_EMPLOYEE_ID, SUPPLIER_ID, SUPPLIER_EMPLOYEE_ID, DELIVERED_AT) VALUES ('supplier_delivery', 'Y', 2, 1, 4, 2, 14, CURRENT_TIMESTAMP);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (2, 20);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (2, 21);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (2, 22);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (2, 23);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (2, 24);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (2, 25);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (2, 26);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (2, 27);
+INSERT INTO C##INVENTORY.DELIVERY_ITEMS (DELIVERY_ID, PHYSICAL_PRODUCT_ID) VALUES (2, 28);
+
+INSERT INTO C##INVENTORY.DELIVERIES (REASON, ACCEPTED, PURCHASE_ORDER_ID, WAREHOUSE_ID, WAREHOUSE_EMPLOYEE_ID, SUPPLIER_ID, SUPPLIER_EMPLOYEE_ID, DELIVERED_AT) VALUES ('customer_return', 'N', NULL, 1, 5, NULL, NULL, CURRENT_TIMESTAMP);
+
+INSERT INTO C##INVENTORY.INVENTORIES (NAME, WAREHOUSE_ID, PLANNED, CREATED_BY, UPDATED_BY) VALUES ('Initial inventory', 1, TO_TIMESTAMP('2022-01-01 10:00:00', 'YYYY-MM-DD HH24:MI:SS'), 16, 16);
+INSERT INTO C##INVENTORY.INVENTORY_MEMBERS (INVENTORY_ID, WAREHOUSE_ID, EMPLOYEE_ID) VALUES (1, 1, 1);
+INSERT INTO C##INVENTORY.INVENTORY_MEMBERS (INVENTORY_ID, WAREHOUSE_ID, EMPLOYEE_ID) VALUES (1, 1, 2);
+INSERT INTO C##INVENTORY.INVENTORY_MEMBERS (INVENTORY_ID, WAREHOUSE_ID, EMPLOYEE_ID) VALUES (1, 1, 3);
+INSERT INTO C##INVENTORY.INVENTORY_OBSERVATIONS (INVENTORY_ID, PHYSICAL_PRODUCT_ID, STATUS, MESSAGE, CREATED_BY, UPDATED_BY) VALUES (1, 2, 'missing', 'Item not found', 13, 13);
+INSERT INTO C##INVENTORY.INVENTORY_OBSERVATIONS (INVENTORY_ID, PHYSICAL_PRODUCT_ID, STATUS, MESSAGE, CREATED_BY, UPDATED_BY) VALUES (1, 3, 'broken', 'Item damaged', 14, 14);
+INSERT INTO C##INVENTORY.INVENTORY_OBSERVATIONS (INVENTORY_ID, PHYSICAL_PRODUCT_ID, STATUS, MESSAGE, CREATED_BY, UPDATED_BY) VALUES (1, 4, 'misplaced', 'Moved at the right place', 14, 14);
+
+INSERT INTO C##INVENTORY.PICKUPS (REASON, ACCEPTED, WAREHOUSE_ID, WAREHOUSE_EMPLOYEE_ID, SUPPLIER_ID, SUPPLIER_EMPLOYEE_ID, DELIVERED_AT) VALUES ('customer_delivery', 'Y', 1, 2, NULL, NULL, CURRENT_TIMESTAMP);
+INSERT INTO C##INVENTORY.PICKUP_ITEMS (PICKUP_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 1);
+INSERT INTO C##INVENTORY.PICKUP_ITEMS (PICKUP_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 4);
+INSERT INTO C##INVENTORY.PICKUP_ITEMS (PICKUP_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 12);
+INSERT INTO C##INVENTORY.PICKUP_ITEMS (PICKUP_ID, PHYSICAL_PRODUCT_ID) VALUES (1, 17);
diff --git a/demos/ecommerce/source_04_catalog_postgres.sql b/demos/ecommerce/source_04_catalog_postgres.sql
new file mode 100644
index 000000000..589b58f12
--- /dev/null
+++ b/demos/ecommerce/source_04_catalog_postgres.sql
@@ -0,0 +1,313 @@
+-- drop everything
+DROP SCHEMA IF EXISTS catalog CASCADE;
+CREATE SCHEMA catalog;
+
+
+-- create the database
+CREATE TABLE catalog.categories (
+ id BIGINT PRIMARY KEY,
+ parent BIGINT REFERENCES catalog.categories (id),
+ depth INT,
+ slug VARCHAR(255) UNIQUE,
+ name VARCHAR(255),
+ description TEXT,
+ description_html TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+CREATE INDEX idx_categories_deleted_at ON catalog.categories (deleted_at);
+COMMENT ON COLUMN catalog.categories.depth IS 'easily accessible information of number of parents';
+
+CREATE TABLE catalog.products (
+ id BIGINT PRIMARY KEY,
+ slug VARCHAR(255) UNIQUE,
+ name VARCHAR(255),
+ category_id BIGINT REFERENCES catalog.categories (id),
+ description TEXT,
+ description_html TEXT,
+ versions JSON,
+ attributes JSON,
+ stock INT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+CREATE INDEX idx_products_deleted_at ON catalog.products (deleted_at);
+COMMENT ON COLUMN catalog.products.description IS 'TODO: handle i18n';
+COMMENT ON COLUMN catalog.products.versions IS 'ex: `[{key: "color", label: "Couleur", values: [{name: "Bleu Azur", value: "#95bbe2"}]}, {key: "storage", name: "Taille", values: [{name: "128GB", value: 128}]}]`';
+COMMENT ON COLUMN catalog.products.attributes IS 'ex: `[{key: "Marque", value: "Google"}]`';
+COMMENT ON COLUMN catalog.products.stock IS 'informative stock, may not be accurate';
+
+CREATE TABLE catalog.product_versions (
+ id BIGINT PRIMARY KEY,
+ product_id BIGINT REFERENCES catalog.products (id),
+ name VARCHAR(255),
+ specs JSON,
+ price DOUBLE PRECISION,
+ stock INT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+CREATE INDEX idx_product_versions_deleted_at ON catalog.product_versions (deleted_at);
+COMMENT ON COLUMN catalog.product_versions.specs IS 'ex: `{color: "Bleu Azur", storage: 128}`';
+COMMENT ON COLUMN catalog.product_versions.stock IS 'informative stock, may not be accurate';
+
+CREATE TABLE catalog.product_cross_sell_options (
+ product_id BIGINT REFERENCES catalog.products (id),
+ product_version_id BIGINT REFERENCES catalog.product_versions (id),
+ label VARCHAR(255),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP,
+ PRIMARY KEY (product_id, product_version_id)
+);
+CREATE INDEX idx_product_cross_sell_options_deleted_at ON catalog.product_cross_sell_options (deleted_at);
+
+CREATE TABLE catalog.product_alternatives (
+ product_id BIGINT REFERENCES catalog.products (id),
+ alternative_product_id BIGINT REFERENCES catalog.products (id),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP,
+ PRIMARY KEY (product_id, alternative_product_id)
+);
+CREATE INDEX idx_product_alternatives_deleted_at ON catalog.product_alternatives (deleted_at);
+
+CREATE TABLE catalog.assets (
+ id BIGINT PRIMARY KEY,
+ kind VARCHAR(50),
+ format VARCHAR(10),
+ size VARCHAR(10),
+ path VARCHAR(255),
+ alt VARCHAR(255),
+ width INT,
+ height INT,
+ weight INT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+CREATE INDEX idx_assets_deleted_at ON catalog.assets (deleted_at);
+
+CREATE TABLE catalog.category_assets (
+ category_id BIGINT REFERENCES catalog.categories (id),
+ asset_id BIGINT REFERENCES catalog.assets (id),
+ placement VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP,
+ PRIMARY KEY (category_id, asset_id)
+);
+CREATE INDEX idx_category_assets_deleted_at ON catalog.category_assets (deleted_at);
+
+CREATE TABLE catalog.product_assets (
+ product_id BIGINT REFERENCES catalog.products (id),
+ asset_id BIGINT REFERENCES catalog.assets (id),
+ placement VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP,
+ PRIMARY KEY (product_id, asset_id)
+);
+CREATE INDEX idx_product_assets_deleted_at ON catalog.product_assets (deleted_at);
+
+CREATE TABLE catalog.product_version_assets (
+ product_version_id BIGINT REFERENCES catalog.product_versions (id),
+ asset_id BIGINT REFERENCES catalog.assets (id),
+ placement VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP,
+ PRIMARY KEY (product_version_id, asset_id)
+);
+CREATE INDEX idx_product_version_assets_deleted_at ON catalog.product_version_assets (deleted_at);
+
+CREATE TABLE catalog.product_reviews (
+ id BIGINT PRIMARY KEY,
+ product_id BIGINT REFERENCES catalog.products (id),
+ product_version_id BIGINT REFERENCES catalog.product_versions (id),
+ invoice_id BIGINT,
+ physical_product_id BIGINT,
+ rating INT,
+ review TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT
+);
+CREATE INDEX idx_product_reviews_deleted_at ON catalog.product_reviews (deleted_at);
+
+CREATE TABLE catalog.product_review_assets (
+ product_review_id BIGINT REFERENCES catalog.product_reviews (id),
+ asset_id BIGINT REFERENCES catalog.assets (id),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ PRIMARY KEY (product_review_id, asset_id)
+);
+CREATE INDEX idx_product_review_assets_deleted_at ON catalog.product_review_assets (deleted_at);
+
+CREATE TABLE catalog.product_review_feedbacks (
+ product_review_id BIGINT REFERENCES catalog.product_reviews (id),
+ kind VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ PRIMARY KEY (product_review_id, created_by, kind)
+);
+CREATE INDEX idx_product_review_feedbacks_deleted_at ON catalog.product_review_feedbacks (deleted_at);
+
+
+-- insert some data
+INSERT INTO catalog.categories (id, parent, depth, slug, name, description, description_html)
+VALUES (1, NULL, 0, 'electronics', 'Electronics', 'All electronic items', '
All electronic items
'),
+ (2, 1, 1, 'phones', 'Phones', 'All kinds of phones', '
All kinds of phones
'),
+ (3, 2, 2, 'smartphones', 'SmartPhones', 'Most advanced phones', '
Most advanced phones
'),
+ (4, 1, 1, 'accessories', 'Accessories', 'Useful accessories', '
Useful accessories
');
+
+INSERT INTO catalog.products (id, slug, name, category_id, description, description_html, versions, attributes, stock)
+VALUES (1, 'pixel-8', 'Pixel 8', 3, 'A high-end smartphone by Google', '
A high-end smartphone by Google
','[{"key": "color", "label": "Color", "values": [{"name": "Obsidian", "value": "#202020"}, {"name": "Hazel", "value": "#8B8D8B"}, {"name": "Rose", "value": "#F1DDD2"}, {"name": "Mint", "value": "#DDF2E5"}]}, {"key": "storage", "label": "Storage", "values": [{"name": "128 GB", "value": 128}, {"name": "256 GB", "value": 256}]}]', '[{"key": "Brand", "value": "Google"}, {"key": "Screen size", "value": "6,2\""}, {"key": "RAM", "value": "8 Go"}, {"key": "Weight", "value": "187 g"}]', 11),
+ (2, 'pixel-8-pro', 'Pixel 8 Pro', 3, 'A high-end smartphone by Google', '
A high-end smartphone by Google
','[]', '[]', 9),
+ (3, 'pixel-8a', 'Pixel 8a', 3, 'A high-end smartphone by Google', '
A high-end smartphone by Google
','[]', '[]', 0),
+ (4, 'pixel-9', 'Pixel 9', 3, 'A high-end smartphone by Google', '
A high-end smartphone by Google
','[]', '[]', 0),
+ (5, 'pixel-9-pro', 'Pixel 9 Pro', 3, 'A high-end smartphone by Google', '
A high-end smartphone by Google
','[]', '[]', 0),
+ (6, 'pixel-9-pro-xl', 'Pixel 9 Pro XL', 3, 'A high-end smartphone by Google', '
A high-end smartphone by Google
','[]', '[]', 0),
+ (7, 'pixel-9-pro-fold', 'Pixel 9 Pro Fold', 3, 'A high-end smartphone by Google', '
A high-end smartphone by Google
','[]', '[]', 0),
+ (8, 'pixel-8-case', 'Pixel 8 Case', 4, 'Protect your phone with class!', '
Protect your phone with class!
','[]', '[]', 5),
+ (9, 'pixel-8-casemate-signature', 'Pixel 8 Case Signature', 4, 'Protect your phone with class!', '
Protect your phone with class!
','[]', '[]', 3);
+
+INSERT INTO catalog.product_versions (id, product_id, name, specs, price, stock)
+VALUES (1, 1, 'Pixel 8 Obsidian 128 Go', '{"color": "Obsidian", "storage": 128}', 599, 2),
+ (2, 1, 'Pixel 8 Obsidian 256 Go', '{"color": "Obsidian", "storage": 256}', 659, 1),
+ (3, 1, 'Pixel 8 Hazel 128 Go', '{"color": "Hazel", "storage": 128}', 599, 2),
+ (4, 1, 'Pixel 8 Hazel 256 Go', '{"color": "Hazel", "storage": 256}', 659, 0),
+ (5, 1, 'Pixel 8 Rose 128 Go', '{"color": "Rose", "storage": 128}', 599, 2),
+ (6, 1, 'Pixel 8 Rose 256 Go', '{"color": "Rose", "storage": 256}', 659, 0),
+ (7, 1, 'Pixel 8 Mint 128 Go', '{"color": "Mint", "storage": 128}', 599, 2),
+ (8, 2, 'Pixel 8 Pro Obsidian 128 Go', '{"color": "Obsidian", "storage": 128}', 899, 3),
+ (9, 2, 'Pixel 8 Pro Obsidian 256 Go', '{"color": "Obsidian", "storage": 256}', 959, 1),
+ (10, 2, 'Pixel 8 Pro Obsidian 512 Go', '{"color": "Obsidian", "storage": 512}', 1099, 1),
+ (11, 2, 'Pixel 8 Pro Bay 128 Go', '{"color": "Bay", "storage": 128}', 899, 1),
+ (12, 2, 'Pixel 8 Pro Bay 256 Go', '{"color": "Bay", "storage": 256}', 959, 1),
+ (13, 2, 'Pixel 8 Pro Porcelain 128 Go', '{"color": "Porcelain", "storage": 128}', 899, 1),
+ (14, 2, 'Pixel 8 Pro Porcelain 256 Go', '{"color": "Porcelain", "storage": 256}', 959, 1),
+ (15, 8, 'Pixel 8 Case Hazel', '{"color": "Hazel"}', 35, 2),
+ (16, 8, 'Pixel 8 Case Coral', '{"color": "Coral"}', 35, 2),
+ (17, 8, 'Pixel 8 Case Mint', '{"color": "Mint"}', 35, 0),
+ (18, 8, 'Pixel 8 Case Rose', '{"color": "Rose"}', 35, 0),
+ (19, 8, 'Pixel 8 Case Charcoal', '{"color": "Charcoal"}', 35, 0),
+ (20, 9, 'Pixel 8 Case Signature Clear', '{}', 30, 2);
+
+INSERT INTO catalog.product_cross_sell_options (product_id, product_version_id, label)
+VALUES (1, 15, 'Protect your phone'),
+ (1, 20, 'Protect your phone');
+
+INSERT INTO catalog.product_alternatives (product_id, alternative_product_id)
+VALUES (1, 2),
+ (1, 3),
+ (1, 4),
+ (2, 1),
+ (2, 3),
+ (2, 4),
+ (3, 1),
+ (3, 2),
+ (3, 4),
+ (4, 1),
+ (4, 5),
+ (4, 6),
+ (4, 7),
+ (5, 1),
+ (5, 4),
+ (5, 6),
+ (5, 7),
+ (6, 1),
+ (6, 4),
+ (6, 5),
+ (6, 7),
+ (7, 1),
+ (7, 4),
+ (7, 5),
+ (7, 6);
+
+INSERT INTO catalog.assets (id, kind, format, size, path, alt, width, height, weight)
+VALUES (1, 'picture', '16:9', 'high', '/images/categories/electronics/banner.png', 'Electronics banner', 1600, 900, 500),
+ (2, 'picture', '16:9', 'high', '/images/categories/electronics/phones/banner.png', 'Phones banner', 1600, 900, 500),
+ (3, 'picture', '16:9', 'high', '/images/categories/electronics/phones/smartphones/banner.png', 'SmartPhones banner', 1600, 900, 500),
+ (4, 'picture', '16:9', 'high', '/images/categories/electronics/accessories/banner.png', 'Accessories banner', 1600, 900, 500),
+ (5, 'picture', '1:1', 'high', '/images/categories/electronics/icon.png', 'Electronics icon', 150, 150, 345),
+ (6, 'picture', '3:4', 'high', '/images/products/pixel-8/obsidian/shot.png', 'Pixel 8 Obsidian shot', 360, 480, 474),
+ (7, 'picture', '3:4', 'high', '/images/products/pixel-8/hazel/shot.png', 'Pixel 8 Hazel shot', 360, 480, 474),
+ (8, 'picture', '3:4', 'high', '/images/products/pixel-8/rose/shot.png', 'Pixel 8 Rose shot', 360, 480, 474),
+ (9, 'picture', '3:4', 'high', '/images/products/pixel-8/mint/shot.png', 'Pixel 8 Mint shot', 360, 480, 474),
+ (10, 'picture', '3:4', 'high', '/images/products/pixel-8-pro/obsidian/shot.png', 'Pixel 8 Pro Obsidian shot', 360, 480, 474),
+ (11, 'picture', '3:4', 'high', '/images/products/pixel-8-pro/bay/shot.png', 'Pixel 8 Pro Bay shot', 360, 480, 474),
+ (12, 'picture', '3:4', 'high', '/images/products/pixel-8-pro/porcelain/shot.png', 'Pixel 8 Pro Porcelain shot', 360, 480, 474),
+ (13, 'picture', '3:4', 'high', '/images/products/pixel-8a/shot.png', 'Pixel 8a shot', 360, 480, 474),
+ (14, 'picture', '3:4', 'high', '/images/products/pixel-9/shot.png', 'Pixel 9 shot', 360, 480, 474),
+ (15, 'picture', '3:4', 'high', '/images/products/pixel-9-pro/shot.png', 'Pixel 9 Pro shot', 360, 480, 474),
+ (16, 'picture', '3:4', 'high', '/images/products/pixel-9-pro-xl/shot.png', 'Pixel 9 Pro XL shot', 360, 480, 474),
+ (17, 'picture', '3:4', 'high', '/images/products/pixel-9-pro-fold/shot.png', 'Pixel 9 Pro Fold shot', 360, 480, 474),
+ (18, 'picture', '3:4', 'high', '/images/products/pixel-8-case/hazel/shot.png', 'Pixel 8 Case Hazel shot', 360, 480, 474),
+ (19, 'picture', '3:4', 'high', '/images/products/pixel-8-case/coral/shot.png', 'Pixel 8 Case Coral shot', 360, 480, 474),
+ (20, 'picture', '3:4', 'high', '/images/products/pixel-8-case/mint/shot.png', 'Pixel 8 Case Mint shot', 360, 480, 474),
+ (21, 'picture', '3:4', 'high', '/images/products/pixel-8-case/rose/shot.png', 'Pixel 8 Case Rose shot', 360, 480, 474),
+ (22, 'picture', '3:4', 'high', '/images/products/pixel-8-case/charcoal/shot.png', 'Pixel 8 Case Charcoal shot', 360, 480, 474),
+ (23, 'picture', '3:4', 'high', '/images/products/pixel-8-casemate-signature/shot.png', 'Pixel 8 Case Signature shot', 360, 480, 474),
+ (24, 'picture', '16:9', 'high', '/uploads/users/102/2024-08-24/9ab74d.jpg', 'User upload', 1280, 720, 12845);
+
+INSERT INTO catalog.category_assets (category_id, asset_id, placement)
+VALUES (1, 1, 'banner'),
+ (2, 2, 'banner'),
+ (3, 3, 'banner'),
+ (4, 4, 'banner'),
+ (1, 5, 'icon');
+
+INSERT INTO catalog.product_assets (product_id, asset_id, placement)
+VALUES (1, 6, 'main'),
+ (2, 10, 'main'),
+ (3, 13, 'main'),
+ (4, 14, 'main'),
+ (5, 15, 'main'),
+ (6, 16, 'main'),
+ (7, 17, 'main'),
+ (8, 18, 'main'),
+ (9, 23, 'main');
+
+INSERT INTO catalog.product_version_assets (product_version_id, asset_id, placement)
+VALUES (1, 6, 'main'),
+ (2, 6, 'main'),
+ (3, 7, 'main'),
+ (4, 7, 'main'),
+ (5, 8, 'main'),
+ (6, 8, 'main'),
+ (7, 9, 'main'),
+ (8, 10, 'main'),
+ (9, 10, 'main'),
+ (10, 10, 'main'),
+ (11, 11, 'main'),
+ (12, 11, 'main'),
+ (13, 12, 'main'),
+ (14, 12, 'main'),
+ (15, 18, 'main'),
+ (16, 19, 'main'),
+ (17, 20, 'main'),
+ (18, 21, 'main'),
+ (19, 22, 'main'),
+ (20, 23, 'main');
+
+INSERT INTO catalog.product_reviews (id, product_id, product_version_id, invoice_id, physical_product_id, rating, review, created_by, updated_by)
+VALUES (1, 1, 1, 1, 1, 5, 'Amazing phone!', 102, 102),
+ (2, 2, 2, 1, 4, 4, 'Great, but too expensive.', 103, 103);
+
+INSERT INTO catalog.product_review_assets (product_review_id, asset_id, created_by)
+VALUES (1, 24, 102);
+
+INSERT INTO catalog.product_review_feedbacks (product_review_id, kind, created_by)
+VALUES (1, 'like', 104),
+ (2, 'report', 105);
diff --git a/demos/ecommerce/source_05_shopping_postgres.sql b/demos/ecommerce/source_05_shopping_postgres.sql
new file mode 100644
index 000000000..24ab6c016
--- /dev/null
+++ b/demos/ecommerce/source_05_shopping_postgres.sql
@@ -0,0 +1,99 @@
+-- drop everything
+DROP SCHEMA IF EXISTS shopping CASCADE;
+CREATE SCHEMA shopping;
+
+
+-- create the database
+CREATE TABLE shopping.carts (
+ id BIGINT PRIMARY KEY,
+ owner_kind VARCHAR(255),
+ owner_id BIGINT,
+ expire_at TIMESTAMP,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ deleted_at TIMESTAMP
+);
+CREATE INDEX idx_carts_deleted_at ON shopping.carts (deleted_at);
+COMMENT ON COLUMN shopping.carts.owner_kind IS 'Devices are used for anonymous carts, otherwise it''s Users';
+
+CREATE TABLE shopping.cart_items (
+ cart_id BIGINT REFERENCES shopping.carts (id),
+ product_version_id BIGINT,
+ quantity INT,
+ price DOUBLE PRECISION,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ PRIMARY KEY (cart_id, product_version_id)
+);
+CREATE INDEX idx_cart_items_deleted_at ON shopping.cart_items (deleted_at);
+COMMENT ON COLUMN shopping.cart_items.price IS 'at the time the product was added to the card, prevent price changes after a product has been added to a cart';
+
+CREATE TABLE shopping.wishlists (
+ id BIGINT PRIMARY KEY,
+ name VARCHAR(255),
+ description TEXT,
+ public BOOLEAN,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT
+);
+CREATE INDEX idx_wishlists_deleted_at ON shopping.wishlists (deleted_at);
+
+CREATE TABLE shopping.wishlist_items (
+ wishlist_id BIGINT REFERENCES shopping.wishlists (id),
+ product_id BIGINT,
+ specs JSON,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ PRIMARY KEY (wishlist_id, product_id)
+);
+CREATE INDEX idx_wishlist_items_deleted_at ON shopping.wishlist_items (deleted_at);
+COMMENT ON COLUMN shopping.wishlist_items.specs IS 'if the user saved specific configuration';
+
+CREATE TABLE shopping.wishlist_members (
+ wishlist_id BIGINT REFERENCES shopping.wishlists (id),
+ user_id BIGINT,
+ rights VARCHAR(50),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ PRIMARY KEY (wishlist_id, user_id)
+);
+CREATE INDEX idx_wishlist_members_deleted_at ON shopping.wishlist_members (deleted_at);
+
+
+-- insert some data
+INSERT INTO shopping.carts (id, owner_kind, owner_id, expire_at)
+VALUES (1, 'Users', 102, CURRENT_TIMESTAMP + INTERVAL '1 day'),
+ (2, 'Devices', 1, CURRENT_TIMESTAMP + INTERVAL '1 day');
+
+INSERT INTO shopping.cart_items (cart_id, product_version_id, quantity, price, created_by, updated_by)
+VALUES (1, 1, 1, 599, 102, 102),
+ (1, 2, 1, 659, 102, 102),
+ (1, 15, 1, 35, 102, 102),
+ (1, 20, 1, 30, 102, 102),
+ (2, 7, 2, 599, NULL, NULL);
+
+INSERT INTO shopping.wishlists (id, name, description, public, created_by, updated_by)
+VALUES (1, 'Bob''s Wishlist', 'Bob''s favorite products', TRUE, 102, 102);
+
+INSERT INTO shopping.wishlist_items (wishlist_id, product_id, specs, created_by)
+VALUES (1, 1, '{"color": "Obsidian", "storage": 128}', 102),
+ (1, 2, '{"color": "Obsidian", "storage": 256}', 102),
+ (1, 8, '{"color": "Coral"}', 102);
+
+INSERT INTO shopping.wishlist_members (wishlist_id, user_id, rights, created_by, updated_by)
+VALUES (1, 102, 'edit', 102, 102),
+ (1, 103, 'view', 102, 102);
diff --git a/demos/ecommerce/source_06_billing_sqlserver.sql b/demos/ecommerce/source_06_billing_sqlserver.sql
new file mode 100644
index 000000000..4e2b523ed
--- /dev/null
+++ b/demos/ecommerce/source_06_billing_sqlserver.sql
@@ -0,0 +1,134 @@
+-- drop everything
+USE master; -- in order to stop using Billing ^^
+DROP DATABASE IF EXISTS Billing;
+
+-- create the database
+CREATE DATABASE Billing;
+USE Billing;
+CREATE SCHEMA [billing];
+
+
+CREATE TABLE [billing].[CustomerAddresses] (
+ [CustomerAddressesId] [bigint] IDENTITY (1,1) PRIMARY KEY,
+ [Name] [nvarchar](255) NOT NULL,
+ [Street] [nvarchar](255) NOT NULL,
+ [City] [nvarchar](255) NOT NULL,
+ [State] [nvarchar](255) NOT NULL,
+ [ZipCode] [nvarchar](20) NOT NULL,
+ [Country] [bigint],
+ [Complements] [text],
+ [CreatedAt] [datetime] DEFAULT CURRENT_TIMESTAMP,
+ [CreatedBy] [bigint],
+ [DeletedAt] [datetime],
+ [DeletedBy] [bigint]
+);
+CREATE INDEX [IdxCustomerAddressesDeletedAt] ON [billing].[CustomerAddresses] ([DeletedAt]);
+
+CREATE TABLE [billing].[Customers] (
+ [CustomerId] [bigint] IDENTITY (1,1) PRIMARY KEY,
+ [Name] [nvarchar](255) NOT NULL,
+ [BillingAddress] [bigint] REFERENCES [billing].[CustomerAddresses] ([CustomerAddressesId]),
+ [Siret] [nvarchar](14),
+ [TVA] [nvarchar](13),
+ [CreatedAt] [datetime] DEFAULT CURRENT_TIMESTAMP,
+ [CreatedBy] [bigint],
+ [UpdatedAt] [datetime] DEFAULT CURRENT_TIMESTAMP,
+ [UpdatedBy] [bigint],
+ [DeletedAt] [datetime],
+ [DeletedBy] [bigint]
+);
+CREATE INDEX [IdxCustomersDeletedAt] ON [billing].[Customers] ([DeletedAt]);
+
+CREATE TABLE [billing].[CustomerMembers] (
+ [CustomerId] [bigint] REFERENCES [billing].[Customers] ([CustomerId]),
+ [UserId] [bigint],
+ [CanEdit] [bit],
+ [CanInvite] [bit],
+ [CanBuy] [bit],
+ [BudgetAllowance] [int],
+ [CreatedAt] [datetime] DEFAULT CURRENT_TIMESTAMP,
+ [CreatedBy] [bigint],
+ [UpdatedAt] [datetime] DEFAULT CURRENT_TIMESTAMP,
+ [UpdatedBy] [bigint],
+ [DeletedAt] [datetime],
+ [DeletedBy] [bigint],
+ PRIMARY KEY ([CustomerId], [UserId])
+);
+CREATE INDEX [IdxCustomerMembersDeletedAt] ON [billing].[CustomerMembers] ([DeletedAt]);
+
+CREATE TABLE [billing].[CustomerPaymentMethods] (
+ [CustomerPaymentMethodId] [bigint] IDENTITY (1,1) PRIMARY KEY,
+ [CustomerId] [bigint] REFERENCES [billing].[Customers] ([CustomerId]),
+ [Name] [nvarchar](255) NOT NULL,
+ [Kind] [nvarchar](50) CHECK ([Kind] IN ('card', 'paypal')),
+ [Details] [nvarchar](MAX) CHECK (ISJSON([Details]) = 1),
+ [CreatedAt] [datetime] DEFAULT CURRENT_TIMESTAMP,
+ [CreatedBy] [bigint],
+ [UpdatedAt] [datetime] DEFAULT CURRENT_TIMESTAMP,
+ [UpdatedBy] [bigint],
+ [DeletedAt] [datetime],
+ [DeletedBy] [bigint]
+);
+CREATE INDEX [IdxCustomerPaymentMethodsDeletedAt] ON [billing].[CustomerPaymentMethods] ([DeletedAt]);
+
+CREATE TABLE [billing].[Invoices] (
+ [InvoiceId] [bigint] IDENTITY (1,1) PRIMARY KEY,
+ [Reference] [nvarchar](50) UNIQUE NOT NULL,
+ [CartId] [bigint],
+ [CustomerId] [bigint] REFERENCES [billing].[Customers] ([CustomerId]),
+ [BillingAddress] [bigint] REFERENCES [billing].[CustomerAddresses] ([CustomerAddressesId]),
+ [TotalPrice] [float],
+ [Currency] [nvarchar](3) CHECK ([Currency] IN ('EUR', 'USD')),
+ [PaidAt] [datetime],
+ [CreatedAt] [datetime] DEFAULT CURRENT_TIMESTAMP,
+ [CreatedBy] [bigint]
+);
+
+CREATE TABLE [billing].[InvoiceLines] (
+ [InvoiceId] [bigint] REFERENCES [billing].[Invoices] ([InvoiceId]),
+ [Index] [int],
+ [ProductVersionId] [bigint],
+ [Description] [text],
+ [Price] [float],
+ [Quantity] [int],
+ PRIMARY KEY ([InvoiceId], [Index])
+);
+
+CREATE TABLE [billing].[Payments] (
+ [PaymentId] [bigint] IDENTITY (1,1) PRIMARY KEY,
+ [InvoiceId] [bigint] REFERENCES [billing].[Invoices] ([InvoiceId]),
+ [PaymentMethodId] [bigint] REFERENCES [billing].[CustomerPaymentMethods] ([CustomerPaymentMethodId]),
+ [Amount] [float],
+ [Currency] [nvarchar](3) CHECK ([Currency] IN ('EUR', 'USD')),
+ [CreatedAt] [datetime] DEFAULT CURRENT_TIMESTAMP
+);
+
+
+-- insert some data
+INSERT INTO [billing].[CustomerAddresses] ([Name], [Street], [City], [State], [ZipCode], [Country], [Complements], [CreatedBy])
+VALUES ('SpongeHome', '124 Conch Street', 'Bikini Bottom', 'Pacific Ocean', '12345', 1, 'Pineapple house next to Squidward', 102);
+
+INSERT INTO [billing].[Customers] ([Name], [BillingAddress], [Siret], [TVA], [CreatedBy], [UpdatedBy])
+VALUES ('SpongeBob', 1, NULL, NULL, 102, 102),
+ ('Krusty Enterprises', 1, '12345678900010', 'FR12345678901', 102, 102);
+
+INSERT INTO [billing].[CustomerMembers] ([CustomerId], [UserId], [CanEdit], [CanInvite], [CanBuy], [BudgetAllowance], [CreatedBy], [UpdatedBy])
+VALUES (1, 102, 1, 1, 1, NULL, 102, 102),
+ (2, 102, 1, 1, 1, NULL, 102, 102),
+ (2, 103, 1, 0, 1, 500, 102, 102);
+
+INSERT INTO [billing].[CustomerPaymentMethods] ([CustomerId], [Name], [Kind], [Details], [CreatedBy], [UpdatedBy])
+VALUES (1, 'PayPal perso', 'paypal', '{"paypal_account": "spongebob@bikinibottom.com"}', 102, 102),
+ (2, 'Company Card', 'card', '{"card_number": "4111111111111111", "expiry_date": "12/28"}', 102, 102);
+
+INSERT INTO [billing].[Invoices] ([Reference], [CartId], [CustomerId], [BillingAddress], [TotalPrice], [Currency], [CreatedBy])
+VALUES ('INV-001', 1, 1, 1, 1323, 'EUR', 102);
+
+INSERT INTO [billing].[InvoiceLines] ([InvoiceId], [Index], [ProductVersionId], [Description], [Price], [Quantity])
+VALUES (1, 1, 1, 'Pixel 8 Obsidian 128 Go', 599, 1),
+ (1, 2, 2, 'Pixel 8 Obsidian 256 Go', 659, 1),
+ (1, 3, 15, 'Pixel 8 Case Hazel', 35, 1),
+ (1, 4, 20, 'Pixel 8 Case Signature Clear', 30, 1);
+
+INSERT INTO [billing].[Payments] ([InvoiceId], [PaymentMethodId], [Amount], [Currency])
+VALUES (1, 1, 1323, 'EUR');
diff --git a/demos/ecommerce/source_07_shipping_mongo.sql b/demos/ecommerce/source_07_shipping_mongo.sql
new file mode 100644
index 000000000..90f9d8632
--- /dev/null
+++ b/demos/ecommerce/source_07_shipping_mongo.sql
@@ -0,0 +1,94 @@
+use shipping;
+
+// drop everything
+db.ShipmentItems.drop();
+db.Shipments.drop();
+db.Carriers.drop();
+
+
+// create the collections
+db.createCollection('Carriers', {
+ validator: {
+ $jsonSchema: {
+ bsonType: "object",
+ required: ["id", "registration", "licensePlate", "cargoWidth", "cargoLength", "cargoHeight", "cargoWeight", "createdAt", "updatedAt"],
+ properties: {
+ id: { bsonType: "number" },
+ registration: { bsonType: "string" },
+ licensePlate: { bsonType: "string" },
+ cargoWidth: { bsonType: "number", description: "inner cargo width in millimeters" },
+ cargoLength: { bsonType: "number", description: "inner cargo length in millimeters" },
+ cargoHeight: { bsonType: "number", description: "inner cargo height in millimeters" },
+ cargoWeight: { bsonType: "number", description: "maximum weight for the cargo, in kilograms" },
+ createdAt: { bsonType: "date" },
+ createdBy: { bsonType: "number" },
+ updatedAt: { bsonType: "date" },
+ updatedBy: { bsonType: "number" },
+ deletedAt: { bsonType: "date" },
+ deletedBy: { bsonType: "number" }
+ }
+ }
+ }
+});
+db.Carriers.createIndex({ registration: 1 });
+
+db.createCollection('Shipments', {
+ validator: {
+ $jsonSchema: {
+ bsonType: "object",
+ required: ["id", "createdAt"],
+ properties: {
+ id: { bsonType: "number" },
+ carrierId: { bsonType: "number" },
+ createdAt: { bsonType: "date" },
+ collectedAt: { bsonType: "date" },
+ collectedBy: { bsonType: "number" },
+ packagedAt: { bsonType: "date" },
+ packagedBy: { bsonType: "number" },
+ loadedAt: { bsonType: "date" },
+ loadedBy: { bsonType: "number" },
+ deliveredAt: { bsonType: "date" },
+ deliveredBy: { bsonType: "number" }
+ }
+ }
+ }
+});
+
+db.createCollection('ShipmentItems', {
+ validator: {
+ $jsonSchema: {
+ bsonType: "object",
+ required: ["shipmentId", "physicalProductId", "invoiceId", "invoiceLine"],
+ properties: {
+ shipmentId: { bsonType: "number" },
+ physicalProductId: { bsonType: "number" },
+ invoiceId: { bsonType: "number" },
+ invoiceLine: { bsonType: "int" },
+ deliveredAt: { bsonType: "date" },
+ deliveredTo: { bsonType: "number", description: "the User who got the delivered package" }
+ }
+ }
+ }
+});
+db.ShipmentItems.createIndex({ shipmentId: 1 });
+db.ShipmentItems.createIndex({ invoiceId: 1 });
+
+
+// insert some data
+db.Carriers.insertMany([
+ {_id: ObjectId("66cb179dfdd0405e567c1938"), id: 1, registration: "KR123456789", licensePlate: "KR-PLATE-001", cargoWidth: 2500, cargoLength: 12000, cargoHeight: 3000, cargoWeight: 24000, createdAt: new Date(), createdBy: 24, updatedAt: new Date(), updatedBy: 24},
+ {_id: ObjectId("66cb179dfdd0405e567c1939"), id: 2, registration: "KR987654321", licensePlate: "KR-PLATE-002", cargoWidth: 2600, cargoLength: 13000, cargoHeight: 3200, cargoWeight: 25000, createdAt: new Date(), createdBy: 24, updatedAt: new Date(), updatedBy: 24},
+ {_id: ObjectId("66cb179dfdd0405e567c193a"), id: 3, registration: "KR112233445", licensePlate: "KR-PLATE-003", cargoWidth: 2400, cargoLength: 11500, cargoHeight: 2900, cargoWeight: 23000, createdAt: new Date(), createdBy: 24, updatedAt: new Date(), updatedBy: 24},
+ {_id: ObjectId("66cb179dfdd0405e567c193b"), id: 4, registration: "KR556677889", licensePlate: "KR-PLATE-004", cargoWidth: 2550, cargoLength: 12500, cargoHeight: 3100, cargoWeight: 24500, createdAt: new Date(), createdBy: 24, updatedAt: new Date(), updatedBy: 24},
+]);
+
+db.Shipments.insertMany([
+ {_id: ObjectId("66cb17a0fdd0405e567c193d"), id: 1, carrierId: 1, createdAt: new Date(), collectedAt: new Date(), collectedBy: 14, packagedAt: new Date(), packagedBy: 14, loadedAt: new Date(), loadedBy: 24, deliveredAt: new Date(), deliveredBy: 24},
+]);
+
+db.ShipmentItems.insertMany([
+ {_id: ObjectId("66cb17a3fdd0405e567c193f"), shipmentId: 1, physicalProductId: 1, invoiceId: 1, invoiceLine: 1, deliveredAt: new Date(), deliveredTo: 102},
+ {_id: ObjectId("66cb17a3fdd0405e567c1940"), shipmentId: 1, physicalProductId: 4, invoiceId: 1, invoiceLine: 2, deliveredAt: new Date(), deliveredTo: 102},
+ {_id: ObjectId("66cb17a3fdd0405e567c1941"), shipmentId: 1, physicalProductId: 12, invoiceId: 1, invoiceLine: 3, deliveredAt: new Date(), deliveredTo: 102},
+ {_id: ObjectId("66cb17a3fdd0405e567c1942"), shipmentId: 1, physicalProductId: 17, invoiceId: 1, invoiceLine: 4, deliveredAt: new Date(), deliveredTo: 102},
+]);
diff --git a/demos/ecommerce/source_08_crm_mysql.sql b/demos/ecommerce/source_08_crm_mysql.sql
new file mode 100644
index 000000000..547a030b7
--- /dev/null
+++ b/demos/ecommerce/source_08_crm_mysql.sql
@@ -0,0 +1,337 @@
+-- drop everything
+DROP SCHEMA IF EXISTS crm;
+
+-- create the database
+CREATE SCHEMA crm;
+USE crm;
+
+CREATE TABLE People (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ name VARCHAR(255) NOT NULL,
+ email VARCHAR(255),
+ phone VARCHAR(20),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ updated_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ INDEX idx_people_deleted_at (deleted_at)
+);
+
+CREATE TABLE Organizations (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ name VARCHAR(255) NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ updated_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ INDEX idx_organizations_deleted_at (deleted_at)
+);
+
+CREATE TABLE OrganizationMembers (
+ person_id BIGINT REFERENCES People (id),
+ organization_id BIGINT REFERENCES Organizations (id),
+ role VARCHAR(255) NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ updated_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ PRIMARY KEY (person_id, organization_id),
+ INDEX idx_organization_members_deleted_at (deleted_at)
+);
+
+CREATE TABLE SocialAccounts (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ network ENUM ('twitter', 'linkedin', 'facebook', 'instagram', 'tiktok', 'snapchat') NOT NULL,
+ username VARCHAR(255) NOT NULL,
+ owner_kind ENUM ('People', 'Organizations'),
+ owner_id BIGINT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ updated_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ INDEX idx_social_accounts_deleted_at (deleted_at)
+);
+
+CREATE TABLE Campaigns (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ name VARCHAR(255) NOT NULL,
+ status ENUM ('draft', 'live', 'paused') NOT NULL,
+ starts TIMESTAMP,
+ ends TIMESTAMP,
+ kind ENUM ('email', 'sms', 'push', 'twitter', 'linkedin', 'instagram', 'facebook') NOT NULL,
+ audience TEXT COMMENT 'DSL for selecting the audience, from crm.People for email & sms or from crm.SocialAccounts for others',
+ subject VARCHAR(255),
+ message TEXT COMMENT 'HTML with templating using recipient info',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ updated_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ INDEX idx_campaigns_deleted_at (deleted_at)
+);
+
+CREATE TABLE CampaignMessages (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ campaign_id BIGINT REFERENCES Campaigns (id),
+ contact_id BIGINT REFERENCES People (id),
+ social_id BIGINT REFERENCES SocialAccounts (id),
+ sent_to VARCHAR(255) NOT NULL COMMENT 'can be email, phone number, social account... depending on campaign kind',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ sent_at TIMESTAMP,
+ opened_at TIMESTAMP,
+ clicked_at TIMESTAMP
+);
+
+CREATE TABLE Issues (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ subject VARCHAR(255) NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ closed_at TIMESTAMP,
+ closed_by BIGINT,
+ INDEX idx_issues_closed_at (closed_at)
+);
+
+CREATE TABLE IssueMessages (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ issue_id BIGINT REFERENCES Issues (id),
+ content TEXT NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ updated_by BIGINT
+);
+
+CREATE TABLE IssueMessageReactions (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ message_id BIGINT REFERENCES IssueMessages (id),
+ kind ENUM ('like', 'dislike') NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ INDEX idx_issue_message_reactions_deleted_at (deleted_at)
+);
+
+CREATE TABLE Discounts (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ name VARCHAR(255) NOT NULL,
+ description VARCHAR(255),
+ kind ENUM ('percentage', 'amount') NOT NULL,
+ value DOUBLE NOT NULL,
+ enable_at TIMESTAMP,
+ expire_at TIMESTAMP,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ updated_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ INDEX idx_discounts_deleted_at (deleted_at)
+);
+
+CREATE TABLE Coupons (
+ id BIGINT PRIMARY KEY AUTO_INCREMENT,
+ discount_id BIGINT REFERENCES Discounts (id),
+ code VARCHAR(255) UNIQUE NOT NULL COMMENT 'public code to use the discount',
+ expire_at TIMESTAMP,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ created_by BIGINT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ updated_by BIGINT,
+ deleted_at TIMESTAMP,
+ deleted_by BIGINT,
+ INDEX idx_coupons_deleted_at (deleted_at)
+);
+
+
+-- insert some data
+INSERT INTO People (id, name, email, phone, created_by, updated_by)
+VALUES (1, 'Han Solo', 'han.solo@rebellion.com', '+33-1-45-67-89-01', 41, 41),
+ (2, 'Luke Skywalker', 'luke.skywalker@rebellion.com', NULL, 41, 41),
+ (3, 'Leia Organa', 'leia.organa@rebellion.com', '+33-2-98-23-45-67', 41, 41),
+ (4, 'Yoda', 'yoda@jediorder.com', NULL, 41, 41),
+ (5, 'Obi-Wan Kenobi', 'obi-wan.kenobi@jediorder.com', '+33-4-72-39-56-78', 41, 41),
+ (6, 'Anakin Skywalker', 'anakin.skywalker@jediorder.com', NULL, 41, 41),
+ (7, 'Darth Vader', 'darth.vader@empire.com', NULL, 41, 41),
+ (8, 'Kylo Ren', 'kylo.ren@firstorder.com', NULL, 41, 41),
+ (9, 'Rey', 'rey@resistance.com', '+33-5-56-23-45-67', 41, 41),
+ (10, 'Finn', 'finn@resistance.com', NULL, 41, 41),
+ (11, 'Poe Dameron', 'poe.dameron@resistance.com', NULL, 41, 41),
+ (12, 'SpongeBob SquarePants', 'spongebob@bikinibottom.com', '+44-20-7946-0958', 41, 41),
+ (13, 'Patrick Star', 'patrick@bikinibottom.com', '+44-20-7946-0321', 41, 41),
+ (14, 'Squidward Tentacles', 'squidward@bikinibottom.com', '+44-161-555-3456', 41, 41),
+ (15, 'Mr Krabs', 'mr.krabs@bikinibottom.com', '+44-121-555-6789', 41, 41),
+ (16, 'Plankton Sheldon', 'plankton@bikinibottom.com', '+44-113-555-4321', 41, 41),
+ (17, 'Sandy Cheeks', 'sandy.cheeks@bikinibottom.com', NULL, 41, 41),
+ (18, 'Michael Scott', 'michael.scott@dundermifflin.com', '+1-202-555-0173', 41, 41),
+ (19, 'Dwight Schrute', 'dwight.schrute@dundermifflin.com', '+1-202-555-0198', 41, 41),
+ (20, 'Jim Halpert', 'jim.halpert@dundermifflin.com', '+1-415-555-1234', 41, 41),
+ (21, 'Pam Beesly', 'pam.beesly@dundermifflin.com', '+1-718-555-5678', 41, 41),
+ (22, 'Stanley Hudson', 'stanley.hudson@dundermifflin.com', '+1-213-555-9012', 41, 41),
+ (23, 'Kevin Malone', 'kevin.malone@dundermifflin.com', '+49-30-1234567', 41, 41),
+ (24, 'Oscar Martinez', 'oscar.martinez@dundermifflin.com', '+49-40-7654321', 41, 41),
+ (25, 'Phyllis Vance', 'phyllis.vance@dundermifflin.com', '+49-69-9876543', 41, 41),
+ (26, 'Angela Martin', 'angela.martin@dundermifflin.com', '+49-89-3456789', 41, 41),
+ (27, 'Andy Bernard', 'andy.bernard@dundermifflin.com', '+49-221-1234567', 41, 41),
+ (28, 'Creed Bratton', 'creed.bratton@dundermifflin.com', NULL, 41, 41),
+ (29, 'Meredith Palmer', 'meredith.palmer@dundermifflin.com', NULL, 41, 41),
+ (30, 'Ryan Howard', 'ryan.howard@dundermifflin.com', NULL, 41, 41),
+ (31, 'Kelly Kapoor', 'kelly.kapoor@dundermifflin.com', NULL, 41, 41),
+ (32, 'Toby Flenderson', 'toby.flenderson@dundermifflin.com', NULL, 41, 41),
+ (33, 'Daryl Philbin', 'daryl.philbin@dundermifflin.com', NULL, 41, 41);
+
+INSERT INTO Organizations (id, name, created_by, updated_by)
+VALUES (1, 'Rebellion', 41, 41),
+ (2, 'Jedi Order', 41, 41),
+ (3, 'Empire', 41, 41),
+ (4, 'First Order', 41, 41),
+ (5, 'Resistance', 41, 41),
+ (6, 'Bikini Bottom Businesses', 41, 41),
+ (7, 'Dunder Mifflin', 41, 41);
+
+INSERT INTO OrganizationMembers (organization_id, person_id, role, created_by, updated_by)
+VALUES (1, 1, 'Captain', 41, 41),
+ (1, 2, 'Jedi Knight', 41, 41),
+ (1, 3, 'Princess', 41, 41),
+ (2, 2, 'Jedi Knight', 41, 41),
+ (2, 4, 'Jedi Master', 41, 41),
+ (2, 5, 'Jedi Master', 41, 41),
+ (2, 6, 'Jedi Knight', 41, 41),
+ (3, 7, 'Sith Lord', 41, 41),
+ (4, 8, 'Supreme Leader', 41, 41),
+ (5, 9, 'Jedi', 41, 41),
+ (5, 10, 'Soldier', 41, 41),
+ (5, 11, 'Pilot', 41, 41),
+ (6, 12, 'Fry Cook', 41, 41),
+ (6, 13, 'Best Friend', 41, 41),
+ (6, 14, 'Cashier', 41, 41),
+ (6, 15, 'Owner', 41, 41),
+ (6, 16, 'Rival Business Owner', 41, 41),
+ (7, 18, 'Regional Manager', 41, 41),
+ (7, 19, 'Assistant to the Regional Manager', 41, 41),
+ (7, 20, 'Sales Representative', 41, 41),
+ (7, 21, 'Receptionist', 41, 41),
+ (7, 22, 'Sales Representative', 41, 41),
+ (7, 23, 'Accountant', 41, 41),
+ (7, 24, 'Accountant', 41, 41),
+ (7, 25, 'Sales Representative', 41, 41),
+ (7, 26, 'Head of Accounting', 41, 41),
+ (7, 27, 'Sales Representative', 41, 41),
+ (7, 28, 'Quality Assurance', 41, 41),
+ (7, 29, 'Supplier Relations', 41, 41),
+ (7, 30, 'Temp', 41, 41),
+ (7, 31, 'Customer Service', 41, 41),
+ (7, 32, 'HR Representative', 41, 41),
+ (7, 33, 'Warehouse Foreman', 41, 41);
+
+INSERT INTO SocialAccounts (owner_kind, owner_id, network, username, created_by, updated_by)
+VALUES ('People', 1, 'twitter', 'han_solo_official', 41, 41),
+ ('People', 1, 'linkedin', 'han-solo-smuggler', 41, 41),
+ ('People', 1, 'instagram', 'captain_solo', 41, 41),
+ ('People', 2, 'twitter', 'luke_skywalker_jedi', 41, 41),
+ ('People', 2, 'facebook', 'luke.skywalker.jedi', 41, 41),
+ ('People', 2, 'instagram', 'jedi_luke', 41, 41),
+ ('People', 3, 'twitter', 'leia_organa_senator', 41, 41),
+ ('People', 3, 'linkedin', 'leia-organa', 41, 41),
+ ('People', 3, 'facebook', 'princess.leia', 41, 41),
+ ('People', 4, 'twitter', 'master_yoda', 41, 41),
+ ('People', 4, 'linkedin', 'yoda-jedi-master', 41, 41),
+ ('People', 4, 'tiktok', 'wise_yoda', 41, 41),
+ ('People', 5, 'twitter', 'obi_wan_kenobi', 41, 41),
+ ('People', 5, 'linkedin', 'obi-wan-kenobi', 41, 41),
+ ('People', 5, 'instagram', 'ben_kenobi', 41, 41),
+ ('People', 6, 'twitter', 'anakin_skywalker', 41, 41),
+ ('People', 6, 'instagram', 'chosen_one_anakin', 41, 41),
+ ('People', 7, 'twitter', 'darth_vader_sith', 41, 41),
+ ('People', 7, 'linkedin', 'darth-vader', 41, 41),
+ ('People', 7, 'tiktok', 'sith_lord_vader', 41, 41),
+ ('People', 8, 'twitter', 'kylo_ren_sith', 41, 41),
+ ('People', 8, 'instagram', 'dark_kylo', 41, 41),
+ ('People', 9, 'twitter', 'rey_jedi', 41, 41),
+ ('People', 9, 'instagram', 'rey.of.light', 41, 41),
+ ('People', 9, 'tiktok', 'rey_skywalker', 41, 41),
+ ('People', 10, 'twitter', 'fn2187_finn', 41, 41),
+ ('People', 10, 'instagram', 'finn_rebellion', 41, 41),
+ ('People', 11, 'twitter', 'poe_dameron_pilot', 41, 41),
+ ('People', 11, 'instagram', 'black_leader_poe', 41, 41),
+ ('Organizations', 1, 'twitter', 'rebellion_alliance', 41, 41),
+ ('Organizations', 1, 'linkedin', 'rebellion-alliance', 41, 41),
+ ('Organizations', 1, 'facebook', 'rebellion.alliance', 41, 41),
+ ('Organizations', 2, 'twitter', 'jedi_order', 41, 41),
+ ('Organizations', 2, 'linkedin', 'jedi-order', 41, 41),
+ ('Organizations', 2, 'facebook', 'jedi.order', 41, 41),
+ ('Organizations', 3, 'twitter', 'galactic_empire', 41, 41),
+ ('Organizations', 3, 'linkedin', 'galactic-empire', 41, 41),
+ ('Organizations', 3, 'instagram', 'galactic_empire', 41, 41),
+ ('Organizations', 4, 'twitter', 'first_order', 41, 41),
+ ('Organizations', 4, 'linkedin', 'first-order', 41, 41),
+ ('Organizations', 4, 'instagram', 'first.order', 41, 41),
+ ('Organizations', 5, 'twitter', 'resistance_alliance', 41, 41),
+ ('Organizations', 5, 'linkedin', 'resistance-alliance', 41, 41),
+ ('Organizations', 5, 'facebook', 'resistance.alliance', 41, 41),
+ ('Organizations', 6, 'twitter', 'bikini_bottom_biz', 41, 41),
+ ('Organizations', 6, 'linkedin', 'bikini-bottom-businesses', 41, 41),
+ ('Organizations', 6, 'instagram', 'bikini_bottom_biz', 41, 41),
+ ('Organizations', 7, 'twitter', 'dunder_mifflin_inc', 41, 41),
+ ('Organizations', 7, 'linkedin', 'dunder-mifflin', 41, 41),
+ ('Organizations', 7, 'facebook', 'dunder.mifflin', 41, 41);
+
+INSERT INTO Campaigns (name, status, starts, ends, kind, audience, subject, message, created_by, updated_by)
+VALUES ('Rebellion Recruitment Drive', 'live', '2024-09-01 08:00:00', '2024-09-15 23:59:59', 'email', 'organization=Rebellion', 'Join the Rebellion!', '
Dear Rebel,
The Rebellion needs you! Join us in the fight against the Empire.
', 41, 41),
+ ('Summer Promotion', 'live', NULL, NULL, 'sms', 'VIP customers', 'Summer Promotion', 'Get 50% off all items!', 41, 41),
+ ('Winter Sale', 'draft', NULL, NULL, 'email', 'all', 'Winter Sale 2024', '
Don\'t miss out on our Winter Sale!
', 41, 41);
+
+INSERT INTO CampaignMessages (campaign_id, contact_id, social_id, sent_to, created_at, sent_at, opened_at, clicked_at)
+VALUES (1, 1, NULL, 'han.solo@rebellion.com', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL),
+ (1, 2, NULL, 'luke.skywalker@rebellion.com', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
+ (1, 3, NULL, 'leia.organa@rebellion.com', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL),
+ (2, 1, NULL, '+33-1-45-67-89-01', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL);
+
+INSERT INTO Issues (subject, created_by, closed_at, closed_by)
+VALUES ('Delayed Shipment of Krabby Patties', 102, '2024-08-05 10:00:00', 105),
+ ('Rebellion Supply Shortage', 37, NULL, NULL),
+ ('Jedi Training Materials Not Delivered', 41, NULL, NULL),
+ ('Empire Recruitment Process Feedback', 43, '2024-08-10 16:30:00', 43),
+ ('First Order Propaganda Issues', 44, NULL, NULL),
+ ('Printer Issues in Scranton Office', 108, '2024-08-06 09:15:00', 108),
+ ('Plankton Complaining About Chum Bucket Sales', 106, NULL, NULL);
+
+INSERT INTO IssueMessages (issue_id, content, created_by, updated_by)
+VALUES (1, 'Shipment of Krabby Patties was delayed by 3 days. Customers are getting impatient.', 102, 102),
+ (1, 'I''ll look into it and make sure it gets sorted out.', 105, 105),
+ (1, 'The issue has been resolved. The shipment is on its way.', 105, 105),
+ (2, 'We are experiencing a shortage of supplies needed for the next mission.', 37, 37),
+ (2, 'We need to secure a new supplier as soon as possible.', 39, 39),
+ (3, 'The training materials for the new Jedi recruits haven''t arrived yet.', 41, 41),
+ (3, 'We need these materials urgently for the next training session.', 38, 38),
+ (4, 'The current recruitment process is too slow and needs to be streamlined.', 43, 43),
+ (4, 'Implemented changes to speed up the process. Issue closed.', 43, 43),
+ (5, 'The propaganda materials sent out last week were not well received.', 44, 44),
+ (5, 'We need to revise our messaging to better align with our core values.', 44, 44),
+ (6, 'The printer on the second floor is constantly jamming. It needs to be replaced.', 108, 108),
+ (6, 'Ordered a new printer. It should arrive tomorrow.', 108, 108),
+ (6, 'Printer issue resolved. New printer installed.', 108, 108),
+ (7, 'Chum Bucket sales have been abysmal. We need a new marketing strategy.', 106, 106),
+ (7, 'Perhaps we could focus on new product offerings or promotions.', 102, 102);
+
+INSERT INTO IssueMessageReactions (message_id, kind, created_by)
+VALUES (2, 'like', 102),
+ (5, 'dislike', 37);
+
+INSERT INTO Discounts (name, description, kind, value, enable_at, expire_at, created_by, updated_by)
+VALUES ('New Year Discount', '10% off on all items', 'percentage', 10.00, '2024-01-01 00:00:00', '2024-01-31 23:59:59', 1, 1),
+ ('Black Friday Deal', 'Flat $50 off', 'amount', 50.00, '2024-11-29 00:00:00', '2024-11-29 23:59:59', 2, 2);
+
+INSERT INTO Coupons (discount_id, code, expire_at, created_by, updated_by)
+VALUES (1, 'NY2024', '2024-01-31 23:59:59', 1, 1),
+ (2, 'BF2024', '2024-11-29 23:59:59', 2, 2);
diff --git a/demos/ecommerce/source_09_analytics_mongo.sql b/demos/ecommerce/source_09_analytics_mongo.sql
new file mode 100644
index 000000000..88ebc5c91
--- /dev/null
+++ b/demos/ecommerce/source_09_analytics_mongo.sql
@@ -0,0 +1,71 @@
+use analytics;
+
+// drop everything
+db.Events.drop();
+db.Entities.drop();
+
+
+// create the collections
+db.createCollection('Events', {
+ validator: {
+ $jsonSchema: {
+ bsonType: "object",
+ required: ["id", "name", "source", "createdAt"],
+ properties: {
+ id: { bsonType: "binData", description: "UUIDv7 to be time ordered" },
+ name: { bsonType: "string", description: "in form of `$context__$object__$action`" },
+ source: { bsonType: "string", enum: ["website", "app", "admin", "job"], description: "the name of the system which emitted this event" },
+ details: { bsonType: "object", description: "any additional info for the event" },
+ entities: {
+ bsonType: "object",
+ description: "{[kind: string]: {id: string, name: string}[]}",
+ additionalProperties: {
+ bsonType: "array",
+ items: {
+ bsonType: "object",
+ required: ["id", "name"],
+ properties: {
+ id: { bsonType: "string" },
+ name: { bsonType: "string" }
+ }
+ }
+ }
+ },
+ createdAt: { bsonType: "date" }
+ }
+ }
+ }
+});
+db.Events.createIndex({ name: 1 });
+db.Events.createIndex({ createdAt: 1 });
+
+db.createCollection('Entities', {
+ validator: {
+ $jsonSchema: {
+ bsonType: "object",
+ required: ["kind", "id", "name", "properties", "createdAt", "updatedAt"],
+ properties: {
+ kind: { bsonType: "string" },
+ id: { bsonType: "string" },
+ name: { bsonType: "string" },
+ properties: { bsonType: "object" },
+ createdAt: { bsonType: "date" },
+ updatedAt: { bsonType: "date" }
+ }
+ }
+ }
+});
+db.Entities.createIndex({ name: 1 });
+
+
+// insert some data
+db.Events.insertMany([
+ {_id: ObjectId("66c19109ab46f11e824ed6a9"), id: "af099435-b2ed-4871-8f27-8bc88e15b68c", name: "user__login__success", source: "website", details: { ip: "192.168.1.1", browser: "Chrome" }, entities: { user: [{ id: "1", name: "Loïc Knuchel", email: "loic@azimutt.app" }] }, createdAt: new Date()},
+ {_id: ObjectId("66c19109ab46f11e824ed6aa"), id: "f35afcfa-92e4-491e-9e51-ed8ac51a9e20", name: "order__purchase__completed", source: "app", details: { amount: 200 }, entities: {user: [{id: "1", name: "Loïc Knuchel"}], cart: [{id: "1", name: "Cart 1", items: 2}], invoice: [{id: "1", name: "INV-001", price: 200, currency: "USD", lines: 1}]}, createdAt: new Date()},
+]);
+
+db.Entities.insertMany([
+ {kind: "user", id: "1", name: "Loïc Knuchel", properties: { email: "loic@azimutt.app" }, createdAt: new Date(), updatedAt: new Date()},
+ {kind: "cart", id: "1", name: "Cart 1", properties: { items: 2 }, createdAt: new Date(), updatedAt: new Date()},
+ {kind: "invoice", id: "1", name: "INV-001", properties: { price: 200, currency: "USD", lines: 1 }, createdAt: new Date(), updatedAt: new Date()},
+]);
diff --git a/demos/ecommerce/source_10_additional_relations.md b/demos/ecommerce/source_10_additional_relations.md
new file mode 100644
index 000000000..9d94587e6
--- /dev/null
+++ b/demos/ecommerce/source_10_additional_relations.md
@@ -0,0 +1,126 @@
+fk C##INVENTORY.EMPLOYEES.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.EMPLOYEES.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.EMPLOYEES.DELETED_BY -> identity.Users.id
+fk C##INVENTORY.WAREHOUSES.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.WAREHOUSES.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.WAREHOUSES.DELETED_BY -> identity.Users.id
+fk C##INVENTORY.HALLS.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.HALLS.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.HALLS.DELETED_BY -> identity.Users.id
+fk C##INVENTORY.AISLES.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.AISLES.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.AISLES.DELETED_BY -> identity.Users.id
+fk C##INVENTORY.RACKS.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.RACKS.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.RACKS.DELETED_BY -> identity.Users.id
+fk C##INVENTORY.SHELVES.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.SHELVES.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.SHELVES.DELETED_BY -> identity.Users.id
+fk C##INVENTORY.SHELF_POSITIONS.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.SHELF_POSITIONS.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.SHELF_POSITIONS.DELETED_BY -> identity.Users.id
+fk C##INVENTORY.WAREHOUSE_EMPLOYEES.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.WAREHOUSE_EMPLOYEES.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.WAREHOUSE_EMPLOYEES.DELETED_BY -> identity.Users.id
+fk C##INVENTORY.WAREHOUSE_IDENTITY_PROOFS.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.SUPPLIER_EMPLOYEES.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.SUPPLIER_EMPLOYEES.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.SUPPLIER_EMPLOYEES.DELETED_BY -> identity.Users.id
+fk C##INVENTORY.PURCHASE_ORDERS.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.PURCHASE_ORDERS.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.PURCHASE_ORDERS.SENT_BY -> identity.Users.id
+fk C##INVENTORY.PURCHASE_ORDERS.VALIDATED_BY -> identity.Users.id
+fk C##INVENTORY.PURCHASE_ORDER_ITEMS.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.PURCHASE_ORDER_ITEMS.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.INVENTORIES.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.INVENTORIES.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.INVENTORY_OBSERVATIONS.CREATED_BY -> identity.Users.id
+fk C##INVENTORY.INVENTORY_OBSERVATIONS.UPDATED_BY -> identity.Users.id
+fk C##INVENTORY.INVENTORY_OBSERVATIONS.DELETED_BY -> identity.Users.id
+fk catalog.products.id -> C##INVENTORY.PRODUCTS.ID
+fk catalog.product_versions.id -> C##INVENTORY.PRODUCT_VERSIONS.ID
+fk catalog.product_reviews.invoice_id -> billing.Invoices.InvoiceId
+fk catalog.product_reviews.physical_product_id -> C##INVENTORY.PHYSICAL_PRODUCTS.ID
+fk catalog.product_reviews.created_by -> identity.Users.id
+fk catalog.product_reviews.updated_by -> identity.Users.id
+fk catalog.product_reviews.deleted_by -> identity.Users.id
+fk catalog.product_review_assets.created_by -> identity.Users.id
+fk catalog.product_review_assets.deleted_by -> identity.Users.id
+fk catalog.product_review_feedbacks.created_by -> identity.Users.id
+fk catalog.product_review_feedbacks.deleted_by -> identity.Users.id
+fk shopping.carts.owner_id -> identity.Devices.id
+fk shopping.carts.owner_id -> identity.Users.id
+fk shopping.cart_items.product_version_id -> catalog.product_versions.id
+fk shopping.cart_items.created_by -> identity.Users.id
+fk shopping.cart_items.updated_by -> identity.Users.id
+fk shopping.cart_items.deleted_by -> identity.Users.id
+fk shopping.wishlists.created_by -> identity.Users.id
+fk shopping.wishlists.updated_by -> identity.Users.id
+fk shopping.wishlists.deleted_by -> identity.Users.id
+fk shopping.wishlist_items.product_id -> catalog.products.id
+fk shopping.wishlist_items.created_by -> identity.Users.id
+fk shopping.wishlist_items.deleted_by -> identity.Users.id
+fk shopping.wishlist_members.user_id -> identity.Users.id
+fk shopping.wishlist_members.created_by -> identity.Users.id
+fk shopping.wishlist_members.updated_by -> identity.Users.id
+fk shopping.wishlist_members.deleted_by -> identity.Users.id
+fk billing.CustomerAddresses.Country -> referential.Countries.CountryId
+fk billing.CustomerAddresses.CreatedBy -> identity.Users.id
+fk billing.CustomerAddresses.DeletedBy -> identity.Users.id
+fk billing.Customers.CreatedBy -> identity.Users.id
+fk billing.Customers.UpdatedBy -> identity.Users.id
+fk billing.Customers.DeletedBy -> identity.Users.id
+fk billing.CustomerMembers.UserId -> identity.Users.id
+fk billing.CustomerMembers.CreatedBy -> identity.Users.id
+fk billing.CustomerMembers.UpdatedBy -> identity.Users.id
+fk billing.CustomerMembers.DeletedBy -> identity.Users.id
+fk billing.CustomerPaymentMethods.CreatedBy -> identity.Users.id
+fk billing.CustomerPaymentMethods.UpdatedBy -> identity.Users.id
+fk billing.CustomerPaymentMethods.DeletedBy -> identity.Users.id
+fk billing.Invoices.CartId -> shopping.carts.id
+fk billing.Invoices.CreatedBy -> identity.Users.id
+fk billing.InvoiceLines.ProductVersionId -> catalog.product_versions.id
+fk shipping.Carriers.createdBy -> identity.Users.id
+fk shipping.Carriers.updatedBy -> identity.Users.id
+fk shipping.Carriers.deletedBy -> identity.Users.id
+fk shipping.Shipments.collectedBy -> identity.Users.id
+fk shipping.Shipments.packagedBy -> identity.Users.id
+fk shipping.Shipments.loadedBy -> identity.Users.id
+fk shipping.Shipments.deliveredBy -> identity.Users.id
+fk shipping.ShipmentItems.physicalProductId -> C##INVENTORY.PHYSICAL_PRODUCTS.ID
+fk shipping.ShipmentItems.invoiceId -> billing.InvoiceLines.InvoiceId
+fk shipping.ShipmentItems.invoiceLine -> billing.InvoiceLines.Index
+fk shipping.ShipmentItems.deliveredTo -> identity.Users.id
+fk crm.People.created_by -> identity.Users.id
+fk crm.People.updated_by -> identity.Users.id
+fk crm.People.deleted_by -> identity.Users.id
+fk crm.Organizations.created_by -> identity.Users.id
+fk crm.Organizations.updated_by -> identity.Users.id
+fk crm.Organizations.deleted_by -> identity.Users.id
+fk crm.OrganizationMembers.created_by -> identity.Users.id
+fk crm.OrganizationMembers.updated_by -> identity.Users.id
+fk crm.OrganizationMembers.deleted_by -> identity.Users.id
+fk crm.SocialAccounts.created_by -> identity.Users.id
+fk crm.SocialAccounts.updated_by -> identity.Users.id
+fk crm.SocialAccounts.deleted_by -> identity.Users.id
+fk crm.Campaigns.created_by -> identity.Users.id
+fk crm.Campaigns.updated_by -> identity.Users.id
+fk crm.Campaigns.deleted_by -> identity.Users.id
+fk crm.Issues.created_by -> identity.Users.id
+fk crm.Issues.closed_by -> identity.Users.id
+fk crm.IssueMessages.created_by -> identity.Users.id
+fk crm.IssueMessages.updated_by -> identity.Users.id
+fk crm.IssueMessageReactions.created_by -> identity.Users.id
+fk crm.IssueMessageReactions.deleted_by -> identity.Users.id
+fk crm.Discounts.created_by -> identity.Users.id
+fk crm.Discounts.updated_by -> identity.Users.id
+fk crm.Discounts.deleted_by -> identity.Users.id
+fk crm.Coupons.created_by -> identity.Users.id
+fk crm.Coupons.updated_by -> identity.Users.id
+fk crm.Coupons.deleted_by -> identity.Users.id
+fk analytics.Events.entities:user:id -> identity.Users.id
+fk analytics.Events.entities:cart:id -> shopping.carts.id
+fk analytics.Events.entities:invoice:id -> billing.Invoices.InvoiceId
+fk analytics.Entities.id -> identity.Users.id # when kind=user
+fk analytics.Entities.id -> shopping.carts.id # when kind=cart
+fk analytics.Entities.id -> billing.Invoices.InvoiceId # when kind=invoice
diff --git a/docs/_assets/azimutt.png b/docs/_assets/azimutt.png
index 99488eedf..989db7da7 100644
Binary files a/docs/_assets/azimutt.png and b/docs/_assets/azimutt.png differ
diff --git a/frontend/src/Components/Organisms/TableRow.elm b/frontend/src/Components/Organisms/TableRow.elm
index 924984f2c..23be39184 100644
--- a/frontend/src/Components/Organisms/TableRow.elm
+++ b/frontend/src/Components/Organisms/TableRow.elm
@@ -811,7 +811,7 @@ viewFooter now source row =
StateSuccess s ->
s.loadedAt
in
- div [ class "px-3 py-1 bg-default-50 text-right italic border-t border-gray-200" ]
+ div [ class "px-3 py-1 bg-default-50 text-right italic border-t border-gray-200 truncate" ]
[ text "from "
, source |> Maybe.mapOrElse (\s -> text s.name) (span [ title (SourceId.toString row.source) ] [ text "unknown source" ])
, text ", "
diff --git a/frontend/src/Models/Project/ColumnType.elm b/frontend/src/Models/Project/ColumnType.elm
index 05a052fb8..15af3d68a 100644
--- a/frontend/src/Models/Project/ColumnType.elm
+++ b/frontend/src/Models/Project/ColumnType.elm
@@ -56,10 +56,10 @@ parse kind =
else if kind |> Regex.matchI "^ARRAY<.*>$" then
Array (parse (kind |> String.dropLeft 6 |> String.dropRight 1))
- else if (kind |> Regex.matchI "^(tiny|medium|long|ci)?text$") || (kind |> Regex.matchI "^character(\\s+varying)?\\s*(\\(\\d+\\))?$") || (kind |> Regex.matchI "^n?(var)?char\\d?\\s*(\\([^)]+\\))?(\\s+CHARACTER\\s+SET\\s+[^ ]+)?$") || (kind |> Regex.matchI "^string(\\(\\d+\\))?$") || (kind |> Regex.matchI "^clob$") then
+ else if (kind |> Regex.matchI "^(tiny|medium|long|ci)?text$") || (kind |> Regex.matchI "^character(\\s+varying)?\\s*(\\(\\d+\\))?$") || (kind |> Regex.matchI "^n?(var)?char\\d?\\s*(\\([^)]+\\))?(\\s+CHARACTER\\s+SET\\s+[^ ]+)?$") || (kind |> Regex.matchI "^string(\\(\\d+\\))?$") || (kind |> Regex.matchI "^clob$") || (kind |> Regex.matchI "^enum\\('.+'\\)$") then
Text
- else if (kind |> Regex.matchI "integer|bit") || (kind |> Regex.matchI "number\\(\\d+(\\s*,\\s*0)?\\)") || (kind |> Regex.matchI "^(small)?serial$") || (kind |> Regex.matchI "^(tiny|small|big)?int\\s*(\\d+)?(\\(\\d+\\))?(\\s+unsigned)?$") then
+ else if (kind |> Regex.matchI "integer|bit") || (kind |> Regex.matchI "number\\(\\d+(\\s*,\\s*0)?\\)") || (kind |> Regex.matchI "^(small)?serial$") || (kind |> Regex.matchI "^(tiny|small|big)?int\\s*(\\d+)?(\\(\\d+\\))?(\\s+unsigned)?$") || (kind |> Regex.matchI "^enum\\(\\d+.*\\)$") then
Int
else if (kind |> Regex.matchI "double(\\s+precision)?") || (kind |> Regex.matchI "number") || (kind |> Regex.matchI "^numeric\\s*(\\(\\d+,\\d+\\))?$") || (kind |> Regex.matchI "^decimal\\s*(\\(\\d+,\\d+\\))?$") then
diff --git a/frontend/src/Models/Project/ProjectSettings.elm b/frontend/src/Models/Project/ProjectSettings.elm
index 485435602..c36256d6b 100644
--- a/frontend/src/Models/Project/ProjectSettings.elm
+++ b/frontend/src/Models/Project/ProjectSettings.elm
@@ -53,7 +53,7 @@ init defaultSchema =
, removedSchemas = []
, removeViews = False
, removedTables = ""
- , hiddenColumns = { list = "created_.+, updated_.+, deleted_.+", max = 15, props = False, relations = False }
+ , hiddenColumns = { list = "created.+, updated.+, deleted.+", max = 15, props = False, relations = False }
, columnOrder = ColumnOrder.OrderByProperty
, relationStyle = RelationStyle.Bezier
, columnBasicTypes = True
diff --git a/frontend/src/PagesComponents/Organization_/Project_/Views/Erd/Memo.elm b/frontend/src/PagesComponents/Organization_/Project_/Views/Erd/Memo.elm
index 3d199e528..d98e97f12 100644
--- a/frontend/src/PagesComponents/Organization_/Project_/Views/Erd/Memo.elm
+++ b/frontend/src/PagesComponents/Organization_/Project_/Views/Erd/Memo.elm
@@ -75,7 +75,7 @@ viewMemo platform conf cursorMode editM memo =
viewMarkdown : String -> Html msg
viewMarkdown content =
- Markdown.prose "prose-img:pointer-events-none" content
+ Markdown.prose "prose-img:pointer-events-none max-w-full" content
handlePointerDown : HtmlId -> PointerEvent -> Msg
diff --git a/frontend/tests/Models/Project/ColumnTypeTest.elm b/frontend/tests/Models/Project/ColumnTypeTest.elm
index dde6b4780..611c6f0c7 100644
--- a/frontend/tests/Models/Project/ColumnTypeTest.elm
+++ b/frontend/tests/Models/Project/ColumnTypeTest.elm
@@ -56,6 +56,8 @@ suite =
, testParse "interval(6)" "Interval"
, testParse "bytea" "Binary"
, testParse "uuid" "Uuid"
+ , testParse "enum(1, 2)" "Int"
+ , testParse "enum('a', 'b')" "Text"
, testParse "bigint[]" "Int[]"
, testParse "character varying(255)[]" "Text[]"
, describe "BigQuery"
diff --git a/frontend/tests/Storage/ProjectV2Test.elm b/frontend/tests/Storage/ProjectV2Test.elm
index 8ba3dae20..12238ab8c 100644
--- a/frontend/tests/Storage/ProjectV2Test.elm
+++ b/frontend/tests/Storage/ProjectV2Test.elm
@@ -134,7 +134,7 @@ project1 =
, ( "empty", Layout [] [] [] [] (time 1202) (time 1203) )
]
, tableRowsSeq = 1
- , settings = ProjectSettings (FindPathSettings 4 "" "") defaultSchema [] False "" (HiddenColumns "created_.+, updated_.+, deleted_.+" 15 False False) OrderByProperty Bezier True False Nothing
+ , settings = ProjectSettings (FindPathSettings 4 "" "") defaultSchema [] False "" (HiddenColumns "created.+, updated.+, deleted.+" 15 False False) OrderByProperty Bezier True False Nothing
, storage = ProjectStorage.Local
, visibility = ProjectVisibility.None
, version = ProjectEncodingVersion.current
@@ -238,7 +238,7 @@ project2 =
, ( "users", Layout [ TableProps ( "public", "users" ) (gridPos 90 100) Size.zeroCanvas Tw.red [ ColumnPath.fromString "id", ColumnPath.fromString "name" ] True False False ] [] [] [] (time 1202) (time 1203) )
]
, tableRowsSeq = 1
- , settings = ProjectSettings (FindPathSettings 4 "users" "created_by") defaultSchema [] False "" (HiddenColumns "created_.+, updated_.+, deleted_.+" 15 False False) OrderByProperty Bezier True False Nothing
+ , settings = ProjectSettings (FindPathSettings 4 "users" "created_by") defaultSchema [] False "" (HiddenColumns "created.+, updated.+, deleted.+" 15 False False) OrderByProperty Bezier True False Nothing
, storage = ProjectStorage.Local
, visibility = ProjectVisibility.None
, version = ProjectEncodingVersion.current
diff --git a/gateway/package-lock.json b/gateway/package-lock.json
index e3a640299..25a493f9b 100644
--- a/gateway/package-lock.json
+++ b/gateway/package-lock.json
@@ -1,24 +1,24 @@
{
"name": "@azimutt/gateway",
- "version": "0.1.16",
+ "version": "0.1.17",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@azimutt/gateway",
- "version": "0.1.16",
+ "version": "0.1.17",
"license": "MIT",
"dependencies": {
"@azimutt/connector-bigquery": "^0.1.1",
"@azimutt/connector-couchbase": "^0.1.1",
- "@azimutt/connector-mariadb": "^0.1.4",
- "@azimutt/connector-mongodb": "^0.1.2",
- "@azimutt/connector-mysql": "^0.1.3",
- "@azimutt/connector-oracle": "^0.1.1",
+ "@azimutt/connector-mariadb": "^0.1.5",
+ "@azimutt/connector-mongodb": "^0.1.3",
+ "@azimutt/connector-mysql": "^0.1.4",
+ "@azimutt/connector-oracle": "^0.1.2",
"@azimutt/connector-postgres": "^0.1.9",
"@azimutt/connector-snowflake": "^0.1.1",
- "@azimutt/connector-sqlserver": "^0.1.2",
- "@azimutt/models": "^0.1.13",
+ "@azimutt/connector-sqlserver": "^0.1.3",
+ "@azimutt/models": "^0.1.14",
"@azimutt/utils": "^0.1.5",
"@fastify/cors": "9.0.1",
"@sinclair/typebox": "0.29.6",
@@ -1049,42 +1049,42 @@
}
},
"node_modules/@azimutt/connector-mariadb": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/@azimutt/connector-mariadb/-/connector-mariadb-0.1.4.tgz",
- "integrity": "sha512-BPJn9pQH2vUW+vTMzustGIaCBLu+9OXRMotEz59htXLNYgg7nYjyoai9br+N8Tt6jbMRSHKAen43tIljK8UhDg==",
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/@azimutt/connector-mariadb/-/connector-mariadb-0.1.5.tgz",
+ "integrity": "sha512-t+LUFDekbtYvcGgb/jASWV0LGMkB4c/bEMEacy6qcoq7u0KssN3JrTOOHCBo8IgURvAGBZ1qA/+FlpKdenCgqg==",
"dependencies": {
- "@azimutt/models": "^0.1.12",
- "@azimutt/utils": "^0.1.4",
+ "@azimutt/models": "^0.1.14",
+ "@azimutt/utils": "^0.1.5",
"mariadb": "3.3.1"
}
},
"node_modules/@azimutt/connector-mongodb": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/@azimutt/connector-mongodb/-/connector-mongodb-0.1.2.tgz",
- "integrity": "sha512-gjdfIOMEzvOA1k3KBo5Ba/ZpC0uXohnA/MPGgU7kBlEZ/p6HAOgrtAothRvrJ56XApAoW0UHp09BtB6uWJmDaw==",
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@azimutt/connector-mongodb/-/connector-mongodb-0.1.3.tgz",
+ "integrity": "sha512-AzHQ4zK5zhIOTo3b7X53pCxq5LKRHx1I/VS16kT5X1JrpjnkMOXKXvWpm8deJhYtVmIzdvmrtxSprO66YK8pZQ==",
"dependencies": {
- "@azimutt/models": "^0.1.12",
- "@azimutt/utils": "^0.1.4",
+ "@azimutt/models": "^0.1.14",
+ "@azimutt/utils": "^0.1.5",
"mongodb": "6.8.0"
}
},
"node_modules/@azimutt/connector-mysql": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@azimutt/connector-mysql/-/connector-mysql-0.1.3.tgz",
- "integrity": "sha512-q3RhGgH2UONi9piyQquwNiFirkCxvWo+uNoSGLSh7sebwXMczDZLNrtK/4HYbjCXDTnBXcx/OGGB8vspGXQcNQ==",
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/@azimutt/connector-mysql/-/connector-mysql-0.1.4.tgz",
+ "integrity": "sha512-ImKpIOvX+i7BnjU1zZBNG+RBYhfFrHM7d0btYMeBzfVI7S9ixFiDERYlDPeuIvZ5D0w2mDZqTzWZemGYJ0p2sg==",
"dependencies": {
- "@azimutt/models": "^0.1.12",
- "@azimutt/utils": "^0.1.4",
+ "@azimutt/models": "^0.1.14",
+ "@azimutt/utils": "^0.1.5",
"mysql2": "3.3.5"
}
},
"node_modules/@azimutt/connector-oracle": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@azimutt/connector-oracle/-/connector-oracle-0.1.1.tgz",
- "integrity": "sha512-lsuDN49RzUyzqCyX0Mzca5sAJKh0Ru02zdCENOivbZ+q5OkOkRJm5qg0eiNJDxlz30kfSY/CPzGEyu8eEtTigw==",
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@azimutt/connector-oracle/-/connector-oracle-0.1.2.tgz",
+ "integrity": "sha512-/aHOZibcORwL2c6bSnefGB5p8cuFayIZA498EVSYLoXlctZhXwbd/KfDS5WXDhNc7YABsUfMUOH08zNmh9hgAA==",
"dependencies": {
- "@azimutt/models": "^0.1.12",
- "@azimutt/utils": "^0.1.4",
+ "@azimutt/models": "^0.1.14",
+ "@azimutt/utils": "^0.1.5",
"oracledb": "6.6.0"
}
},
@@ -1110,19 +1110,19 @@
}
},
"node_modules/@azimutt/connector-sqlserver": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/@azimutt/connector-sqlserver/-/connector-sqlserver-0.1.2.tgz",
- "integrity": "sha512-idBQtULAvKuXUoI7JfWAoMCXWfVolPMNAbogxe/oxXqLKjKHbDuohyLwgyFmBpEEwr2dwSayucIp1Zfw8zh5AQ==",
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@azimutt/connector-sqlserver/-/connector-sqlserver-0.1.3.tgz",
+ "integrity": "sha512-gFvHYZRyRRHigQt2Aw586Nr8qA9rtg10jN1hmjVZXck0m1f6tZzBjd9dTBWR3fEIMZYse4qiv1XwxYyBykSYhg==",
"dependencies": {
- "@azimutt/models": "^0.1.12",
- "@azimutt/utils": "^0.1.4",
+ "@azimutt/models": "^0.1.14",
+ "@azimutt/utils": "^0.1.5",
"mssql": "11.0.1"
}
},
"node_modules/@azimutt/models": {
- "version": "0.1.13",
- "resolved": "https://registry.npmjs.org/@azimutt/models/-/models-0.1.13.tgz",
- "integrity": "sha512-ppElflwEJdeFaiqh0NhpY0foI/mcN5W3V5wWyHDMwAYYqw0C1WLcL2KHsgQGWGbRNFTN96bjkzZG57S8U+M3Tg==",
+ "version": "0.1.14",
+ "resolved": "https://registry.npmjs.org/@azimutt/models/-/models-0.1.14.tgz",
+ "integrity": "sha512-rzF1OYLZufeBlf6v32eZ/9HI5+muCO1oJ4a6iS3j5b4J5hjJoN3jWwPUmA3+n0Wp2VY16UV/whWiWM+hlsL+/Q==",
"dependencies": {
"@azimutt/utils": "^0.1.5",
"openai": "4.54.0",
diff --git a/gateway/package.json b/gateway/package.json
index 5d034f7c5..4e654e33b 100644
--- a/gateway/package.json
+++ b/gateway/package.json
@@ -1,6 +1,6 @@
{
"name": "@azimutt/gateway",
- "version": "0.1.16",
+ "version": "0.1.17",
"description": "A Gateway to proxy database access for Azimutt frontend",
"keywords": [
"database",
@@ -42,14 +42,14 @@
"dependencies": {
"@azimutt/connector-bigquery": "^0.1.1",
"@azimutt/connector-couchbase": "^0.1.1",
- "@azimutt/connector-mariadb": "^0.1.4",
- "@azimutt/connector-mongodb": "^0.1.2",
- "@azimutt/connector-mysql": "^0.1.3",
- "@azimutt/connector-oracle": "^0.1.1",
+ "@azimutt/connector-mariadb": "^0.1.5",
+ "@azimutt/connector-mongodb": "^0.1.3",
+ "@azimutt/connector-mysql": "^0.1.4",
+ "@azimutt/connector-oracle": "^0.1.2",
"@azimutt/connector-postgres": "^0.1.9",
"@azimutt/connector-snowflake": "^0.1.1",
- "@azimutt/connector-sqlserver": "^0.1.2",
- "@azimutt/models": "^0.1.13",
+ "@azimutt/connector-sqlserver": "^0.1.3",
+ "@azimutt/models": "^0.1.14",
"@azimutt/utils": "^0.1.5",
"@fastify/cors": "9.0.1",
"@sinclair/typebox": "0.29.6",
diff --git a/libs/connector-mariadb/package.json b/libs/connector-mariadb/package.json
index f1d25274f..19e804d2f 100644
--- a/libs/connector-mariadb/package.json
+++ b/libs/connector-mariadb/package.json
@@ -1,6 +1,6 @@
{
"name": "@azimutt/connector-mariadb",
- "version": "0.1.4",
+ "version": "0.1.5",
"description": "Connect to MariaDB, extract schema, run analysis and queries",
"keywords": [],
"homepage": "https://azimutt.app",
diff --git a/libs/connector-mariadb/src/index.ts b/libs/connector-mariadb/src/index.ts
index a7514941f..eef0204e7 100644
--- a/libs/connector-mariadb/src/index.ts
+++ b/libs/connector-mariadb/src/index.ts
@@ -10,7 +10,6 @@ import {
DatabaseQuery,
DatabaseUrlParsed,
EntityRef,
- parseDatabaseOptions,
QueryAnalyze,
QueryResults,
zodParseAsync
@@ -26,7 +25,7 @@ export const mariadb: Connector = {
const urlOptions = url.options || {}
const options: ConnectorSchemaOpts = {
...opts,
- schema: opts.schema || urlOptions['schema'],
+ schema: opts.schema || urlOptions['schema'] || url.db,
entity: opts.entity || urlOptions['table']
}
return connect(application, url, getSchema(options), options).then(zodParseAsync(Database))
diff --git a/libs/connector-mongodb/package.json b/libs/connector-mongodb/package.json
index 0f58c5f66..f0535c698 100644
--- a/libs/connector-mongodb/package.json
+++ b/libs/connector-mongodb/package.json
@@ -1,6 +1,6 @@
{
"name": "@azimutt/connector-mongodb",
- "version": "0.1.2",
+ "version": "0.1.3",
"description": "Connect to MongoDB, extract schema, run analysis and queries",
"keywords": [],
"homepage": "https://azimutt.app",
diff --git a/libs/connector-mongodb/src/mongodb.ts b/libs/connector-mongodb/src/mongodb.ts
index 9e053163c..230cbd9b6 100644
--- a/libs/connector-mongodb/src/mongodb.ts
+++ b/libs/connector-mongodb/src/mongodb.ts
@@ -105,7 +105,7 @@ async function inferCollectionMixed(collection: Collection, mixed: MixedCollecti
const pk = attrs.find(a => a.name === '_id')
const count = await countDocuments(collection, mixed, opts)
return removeUndefined({
- database: collection.dbName,
+ schema: collection.dbName,
name: mixed ? `${collection.collectionName}__${mixed.attribute}__${mixed.value}` : collection.collectionName,
kind: undefined,
def: undefined,
diff --git a/libs/connector-mysql/package.json b/libs/connector-mysql/package.json
index ee5b67fec..6aed50b62 100644
--- a/libs/connector-mysql/package.json
+++ b/libs/connector-mysql/package.json
@@ -1,6 +1,6 @@
{
"name": "@azimutt/connector-mysql",
- "version": "0.1.3",
+ "version": "0.1.4",
"description": "Connect to MySQL, extract schema, run analysis and queries",
"keywords": [],
"homepage": "https://azimutt.app",
diff --git a/libs/connector-mysql/src/index.ts b/libs/connector-mysql/src/index.ts
index e65870836..fe26f8e01 100644
--- a/libs/connector-mysql/src/index.ts
+++ b/libs/connector-mysql/src/index.ts
@@ -25,7 +25,7 @@ export const mysql: Connector = {
const urlOptions = url.options || {}
const options: ConnectorSchemaOpts = {
...opts,
- schema: opts.schema || urlOptions['schema'],
+ schema: opts.schema || urlOptions['schema'] || url.db,
entity: opts.entity || urlOptions['table']
}
return connect(application, url, getSchema(options), options).then(zodParseAsync(Database))
diff --git a/libs/connector-oracle/package.json b/libs/connector-oracle/package.json
index 13efda742..5c4fa29ac 100644
--- a/libs/connector-oracle/package.json
+++ b/libs/connector-oracle/package.json
@@ -1,6 +1,6 @@
{
"name": "@azimutt/connector-oracle",
- "version": "0.1.1",
+ "version": "0.1.2",
"description": "Connect to Oracle, extract schema, run analysis and queries",
"keywords": [],
"homepage": "https://azimutt.app",
diff --git a/libs/connector-oracle/src/index.ts b/libs/connector-oracle/src/index.ts
index f496b7e06..fed5c2bce 100644
--- a/libs/connector-oracle/src/index.ts
+++ b/libs/connector-oracle/src/index.ts
@@ -26,7 +26,7 @@ export const oracle: Connector = {
const urlOptions = url.options || {}
const options: ConnectorSchemaOpts = {
...opts,
- schema: opts.schema || urlOptions['schema'] || urlOptions['owner'],
+ schema: opts.schema || urlOptions['schema'] || urlOptions['owner'] || urlOptions['user'],
entity: opts.entity || urlOptions['table'],
}
return connect(application, url, getSchema(options), options).then(zodParseAsync(Database))
diff --git a/libs/connector-oracle/src/oracle.ts b/libs/connector-oracle/src/oracle.ts
index 8d151fe7c..eebbc279a 100644
--- a/libs/connector-oracle/src/oracle.ts
+++ b/libs/connector-oracle/src/oracle.ts
@@ -448,7 +448,7 @@ export const getIndexes = (opts: ScopeOpts) => async (conn: Conn): Promise
{
test.skip('execQuery2', async () => {
const query = 'SELECT *\nFROM "C##AZIMUTT"."USERS"\nFETCH FIRST 100 ROWS ONLY;'
const results = await connect(application, url, execQuery(query, []), opts)
+ console.log('results', results)
expect(results.attributes).toEqual([
{name: 'ID', ref: {schema: 'C##AZIMUTT', entity: 'USERS', attribute: ['ID']}},
{name: 'NAME', ref: {schema: 'C##AZIMUTT', entity: 'USERS', attribute: ['NAME']}},
diff --git a/libs/connector-oracle/src/query.ts b/libs/connector-oracle/src/query.ts
index 996efe110..af7d104c7 100644
--- a/libs/connector-oracle/src/query.ts
+++ b/libs/connector-oracle/src/query.ts
@@ -1,4 +1,5 @@
-import {buildQueryAttributes, QueryResults} from "@azimutt/models";
+import * as oracledb from "oracledb";
+import {AttributeValue, buildQueryAttributes, QueryResults} from "@azimutt/models";
import {Conn, QueryResultArrayMode} from "./connect";
export const execQuery = (query: string, parameters: any[]) => (conn: Conn): Promise => {
@@ -7,6 +8,15 @@ export const execQuery = (query: string, parameters: any[]) => (conn: Conn): Pro
async function buildResults(conn: Conn, query: string, result: QueryResultArrayMode): Promise {
const attributes = buildQueryAttributes(result.fields, query)
- const rows = result.rows.map(row => attributes.reduce((acc, col, i) => ({...acc, [col.name]: row[i]}), {}))
+ const rows = await Promise.all(result.rows.map(async row => Object.fromEntries(await Promise.all(attributes.map((attr, i) => buildValue(row[i]).then(v => [attr.name, v]))))))
return {query, attributes, rows}
}
+
+async function buildValue(v: AttributeValue): Promise {
+ if (typeof v === 'object' && v !== null && v.constructor.name === 'Lob') return getLobData(v as oracledb.Lob)
+ return v
+}
+
+function getLobData(lob: oracledb.Lob): AttributeValue {
+ return lob.getData().then(data => typeof data === 'string' ? data : data.toString())
+}
diff --git a/libs/connector-sqlserver/package.json b/libs/connector-sqlserver/package.json
index 93ba63b1a..b373437d6 100644
--- a/libs/connector-sqlserver/package.json
+++ b/libs/connector-sqlserver/package.json
@@ -1,6 +1,6 @@
{
"name": "@azimutt/connector-sqlserver",
- "version": "0.1.2",
+ "version": "0.1.3",
"description": "Connect to SQL Server, extract schema, run analysis and queries",
"keywords": [],
"homepage": "https://azimutt.app",
diff --git a/libs/connector-sqlserver/src/sqlserver.test.ts b/libs/connector-sqlserver/src/sqlserver.test.ts
index e8c42a91a..7af535a1d 100644
--- a/libs/connector-sqlserver/src/sqlserver.test.ts
+++ b/libs/connector-sqlserver/src/sqlserver.test.ts
@@ -1,7 +1,7 @@
import {describe, expect, test} from "@jest/globals";
import {ConnectorSchemaOpts, DatabaseUrlParsed, parseDatabaseUrl} from "@azimutt/models";
import {connect} from "./connect";
-import {getSchema} from "./sqlserver";
+import {getComments, getSchema} from "./sqlserver";
import {application, logger} from "./constants.test";
describe('sqlserver', () => {
@@ -14,4 +14,9 @@ describe('sqlserver', () => {
console.log('schema', schema)
expect(schema.entities?.length).toEqual(13)
})
+ test.skip('getComments', async () => {
+ const comments = await connect(application, url, getComments(opts), opts)
+ console.log(`${comments.length} comments`, comments)
+ expect(comments.length).toEqual(4)
+ })
})
diff --git a/libs/connector-sqlserver/src/sqlserver.ts b/libs/connector-sqlserver/src/sqlserver.ts
index bfed3d27e..56e9204fa 100644
--- a/libs/connector-sqlserver/src/sqlserver.ts
+++ b/libs/connector-sqlserver/src/sqlserver.ts
@@ -124,8 +124,7 @@ const getColumns = (opts: ConnectorSchemaOpts) => async (conn: Conn): Promise async (conn: Conn): Promi
, ic.key_ordinal as column_index
FROM sys.indexes i
JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
- WHERE ${scopeWhere({schema: 'OBJECT_SCHEMA_NAME(i.object_id)', entity: 'OBJECT_NAME(i.object_id)'}, opts)}
- ORDER BY table_schema, table_name, constraint_name;`, [], 'getIndexColumns'
+ WHERE ${scopeWhere({schema: 'OBJECT_SCHEMA_NAME(i.object_id)', entity: 'OBJECT_NAME(i.object_id)'}, opts)};`, [], 'getIndexColumns'
).catch(handleError(`Failed to get index columns (pks, uniques & indexes)`, [], opts))
}
@@ -240,8 +238,7 @@ const getChecks = (opts: ConnectorSchemaOpts) => async (conn: Conn): Promise async (conn: Conn): Promise => {
+export const getComments = (opts: ConnectorSchemaOpts) => async (conn: Conn): Promise => {
// https://learn.microsoft.com/sql/relational-databases/system-catalog-views/extended-properties-catalog-views-sys-extended-properties
// https://learn.microsoft.com/sql/relational-databases/system-catalog-views/sys-objects-transact-sql
- // https://learn.microsoft.com/sql/relational-databases/system-compatibility-views/sys-sysusers-transact-sql
+ // https://learn.microsoft.com/sql/relational-databases/system-catalog-views/schemas-catalog-views-sys-schemas?view=sql-server-ver16
// https://learn.microsoft.com/sql/relational-databases/system-catalog-views/sys-columns-transact-sql
return conn.query(`
SELECT s.name AS table_schema
@@ -281,14 +278,13 @@ const getComments = (opts: ConnectorSchemaOpts) => async (conn: Conn): Promise async (conn: Conn):
JOIN sys.tables tab2 ON tab2.object_id = fkc.referenced_object_id
JOIN sys.schemas sch2 ON tab2.schema_id = sch2.schema_id
JOIN sys.columns col2 ON col2.column_id = referenced_column_id AND col2.object_id = tab2.object_id
- WHERE ${scopeWhere({schema: 'sch1.name', entity: 'tab1.name'}, opts)}
- ORDER BY src_schema, src_table, src_column;`, [], 'getForeignKeyColumns'
+ WHERE ${scopeWhere({schema: 'sch1.name', entity: 'tab1.name'}, opts)};`, [], 'getForeignKeyColumns'
).catch(handleError(`Failed to get foreign keys`, [], opts))
}
diff --git a/libs/models/package.json b/libs/models/package.json
index a30c6f2ef..1cc863545 100644
--- a/libs/models/package.json
+++ b/libs/models/package.json
@@ -1,6 +1,6 @@
{
"name": "@azimutt/models",
- "version": "0.1.13",
+ "version": "0.1.14",
"description": "Define a standard database models for Azimutt.",
"keywords": [],
"homepage": "https://azimutt.app",
diff --git a/libs/models/src/databaseUrl.test.ts b/libs/models/src/databaseUrl.test.ts
index f61f4b7b0..59860cf80 100644
--- a/libs/models/src/databaseUrl.test.ts
+++ b/libs/models/src/databaseUrl.test.ts
@@ -117,6 +117,16 @@ describe('databaseUrl', () => {
host: 'localhost',
port: 5221,
})
+ expect(parseDatabaseUrl('oracle:thin:system/oracle@localhost:1521/FREE?schema=C##AZIMUTT')).toEqual({
+ full: 'oracle:thin:system/oracle@localhost:1521/FREE?schema=C##AZIMUTT',
+ kind: 'oracle',
+ user: 'system',
+ pass: 'oracle',
+ host: 'localhost',
+ port: 1521,
+ db: 'FREE',
+ options: {schema: 'C##AZIMUTT'}
+ })
})
test('parse postgres url', () => {
expect(parseDatabaseUrl('postgres://postgres0.example.com')).toEqual({
diff --git a/libs/models/src/databaseUrl.ts b/libs/models/src/databaseUrl.ts
index dcc9cb888..68fb83786 100644
--- a/libs/models/src/databaseUrl.ts
+++ b/libs/models/src/databaseUrl.ts
@@ -22,7 +22,7 @@ const couchbaseRegexxxxxxxxxxx = /^couchbases?:\/\/(?:([^:]+):([^@]*)@)?([^:/?&]
const mariadbRegexxxxxxx = /^(?:jdbc:)?mariadb:\/\/(?:([^:]+):([^@]*)@)?([^:/?&]+)(?::(\d+))?(?:\/([^?]+))?(?:\?(.+))?$/
const mongoRegexxxxxxxxxx = /^mongodb(?:\+srv)?:\/\/(?:([^:]+):([^@]*)@)?([^:/?&]+)(?::(\d+))?(?:\/([^?]+))?(?:\?(.+))?$/
const mysqlRegexxxxxxxxxxx = /^(?:jdbc:)?mysql:\/\/(?:([^:]+):([^@]*)@)?([^:/?&]+)(?::(\d+))?(?:\/([^?]+))?(?:\?(.+))?$/
-const oracleRegexxxxxx = /^(?:jdbc:)?oracle:thin:(?:([^\/]+)\/([^@]+)@)?(?:\/\/)?([^:\/]+)(?::(\d+))?(?:[\/:]([^?]+))?$/
+const oracleRegexxxxxx = /^(?:jdbc:)?oracle:thin:(?:([^\/]+)\/([^@]+)@)?(?:\/\/)?([^:\/]+)(?::(\d+))?(?:[\/:]([^?]+))?(?:\?(.+))?$/
const postgresRe = /^(?:jdbc:)?postgres(?:ql)?:\/\/(?:([^:]+):([^@]*)@)?([^:/?&]+)(?::(\d+))?(?:\/([^?]+))?(?:\?(.+))?$/
const sqlserver = /^(?:jdbc:)?sqlserver(?:ql)?:\/\/(?:([^:]+):([^@]*)@)?([^:/?&]+)(?::(\d+))?(?:\/([^?]+))?(?:\?(.+))?$/
const snowflakeRegexxx = /^(?:jdbc:)?snowflake:\/\/(?:([^:]+):([^@]*)@)?([^:/?&]+)(?::(\d+))?(?:\/([^?]+))?(?:\?(.+))?$/
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 92381bb74..722d660d3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -397,17 +397,17 @@ importers:
specifier: ^0.1.1
version: 0.1.1
'@azimutt/connector-mariadb':
- specifier: ^0.1.4
- version: 0.1.4
+ specifier: ^0.1.5
+ version: 0.1.5
'@azimutt/connector-mongodb':
- specifier: ^0.1.2
- version: 0.1.2(socks@2.8.3)
- '@azimutt/connector-mysql':
specifier: ^0.1.3
- version: 0.1.3
+ version: 0.1.3(socks@2.8.3)
+ '@azimutt/connector-mysql':
+ specifier: ^0.1.4
+ version: 0.1.4
'@azimutt/connector-oracle':
- specifier: ^0.1.1
- version: 0.1.1
+ specifier: ^0.1.2
+ version: 0.1.2
'@azimutt/connector-postgres':
specifier: ^0.1.9
version: 0.1.9(pg-native@3.0.1)
@@ -415,11 +415,11 @@ importers:
specifier: ^0.1.1
version: 0.1.1(asn1.js@5.4.1)
'@azimutt/connector-sqlserver':
- specifier: ^0.1.2
- version: 0.1.2
+ specifier: ^0.1.3
+ version: 0.1.3
'@azimutt/models':
- specifier: ^0.1.13
- version: 0.1.13
+ specifier: ^0.1.14
+ version: 0.1.14
'@azimutt/utils':
specifier: ^0.1.5
version: 0.1.5
@@ -1198,17 +1198,17 @@ packages:
'@azimutt/connector-couchbase@0.1.1':
resolution: {integrity: sha512-c0ZSWRqR+I6qzrgROUpoAuzkTWdPEz6Jz/Na+0Lt/1Uqyr9Vj1JSm4dwgh53RN0G8kC4HPJWpD8gC58Z0tyErA==}
- '@azimutt/connector-mariadb@0.1.4':
- resolution: {integrity: sha512-BPJn9pQH2vUW+vTMzustGIaCBLu+9OXRMotEz59htXLNYgg7nYjyoai9br+N8Tt6jbMRSHKAen43tIljK8UhDg==}
+ '@azimutt/connector-mariadb@0.1.5':
+ resolution: {integrity: sha512-t+LUFDekbtYvcGgb/jASWV0LGMkB4c/bEMEacy6qcoq7u0KssN3JrTOOHCBo8IgURvAGBZ1qA/+FlpKdenCgqg==}
- '@azimutt/connector-mongodb@0.1.2':
- resolution: {integrity: sha512-gjdfIOMEzvOA1k3KBo5Ba/ZpC0uXohnA/MPGgU7kBlEZ/p6HAOgrtAothRvrJ56XApAoW0UHp09BtB6uWJmDaw==}
+ '@azimutt/connector-mongodb@0.1.3':
+ resolution: {integrity: sha512-AzHQ4zK5zhIOTo3b7X53pCxq5LKRHx1I/VS16kT5X1JrpjnkMOXKXvWpm8deJhYtVmIzdvmrtxSprO66YK8pZQ==}
- '@azimutt/connector-mysql@0.1.3':
- resolution: {integrity: sha512-q3RhGgH2UONi9piyQquwNiFirkCxvWo+uNoSGLSh7sebwXMczDZLNrtK/4HYbjCXDTnBXcx/OGGB8vspGXQcNQ==}
+ '@azimutt/connector-mysql@0.1.4':
+ resolution: {integrity: sha512-ImKpIOvX+i7BnjU1zZBNG+RBYhfFrHM7d0btYMeBzfVI7S9ixFiDERYlDPeuIvZ5D0w2mDZqTzWZemGYJ0p2sg==}
- '@azimutt/connector-oracle@0.1.1':
- resolution: {integrity: sha512-lsuDN49RzUyzqCyX0Mzca5sAJKh0Ru02zdCENOivbZ+q5OkOkRJm5qg0eiNJDxlz30kfSY/CPzGEyu8eEtTigw==}
+ '@azimutt/connector-oracle@0.1.2':
+ resolution: {integrity: sha512-/aHOZibcORwL2c6bSnefGB5p8cuFayIZA498EVSYLoXlctZhXwbd/KfDS5WXDhNc7YABsUfMUOH08zNmh9hgAA==}
'@azimutt/connector-postgres@0.1.9':
resolution: {integrity: sha512-ZVl0/R741vMU1E7wwvXaAWnvvfuiwvPR1/MklaVXhG4ijCzjpi0T+vvk7qlX8W0uJeSqG+z/1UMBF1sxRPE27Q==}
@@ -1216,11 +1216,11 @@ packages:
'@azimutt/connector-snowflake@0.1.1':
resolution: {integrity: sha512-S8zM9fppxFGDb5hLO6o0lUfY/5MELncjy7KuK9VBcpKbf7OLO+YWpj8pnc0NloeEemdSyL8G4CKyXzEEgN36ZQ==}
- '@azimutt/connector-sqlserver@0.1.2':
- resolution: {integrity: sha512-idBQtULAvKuXUoI7JfWAoMCXWfVolPMNAbogxe/oxXqLKjKHbDuohyLwgyFmBpEEwr2dwSayucIp1Zfw8zh5AQ==}
+ '@azimutt/connector-sqlserver@0.1.3':
+ resolution: {integrity: sha512-gFvHYZRyRRHigQt2Aw586Nr8qA9rtg10jN1hmjVZXck0m1f6tZzBjd9dTBWR3fEIMZYse4qiv1XwxYyBykSYhg==}
- '@azimutt/models@0.1.13':
- resolution: {integrity: sha512-ppElflwEJdeFaiqh0NhpY0foI/mcN5W3V5wWyHDMwAYYqw0C1WLcL2KHsgQGWGbRNFTN96bjkzZG57S8U+M3Tg==}
+ '@azimutt/models@0.1.14':
+ resolution: {integrity: sha512-rzF1OYLZufeBlf6v32eZ/9HI5+muCO1oJ4a6iS3j5b4J5hjJoN3jWwPUmA3+n0Wp2VY16UV/whWiWM+hlsL+/Q==}
'@azimutt/utils@0.1.5':
resolution: {integrity: sha512-QhfsGJqlfnUP+nOyhVXANNhBvMqYnQO6B6KoaOnZ+f0BpDGchs4BjMYy81rFYIPzoNSRg7qiQGmbw+zLjpZtBg==}
@@ -9709,7 +9709,7 @@ snapshots:
'@azimutt/connector-bigquery@0.1.1':
dependencies:
- '@azimutt/models': 0.1.13
+ '@azimutt/models': 0.1.14
'@azimutt/utils': 0.1.5
'@google-cloud/bigquery': 7.7.0(encoding@0.1.13)
transitivePeerDependencies:
@@ -9718,24 +9718,24 @@ snapshots:
'@azimutt/connector-couchbase@0.1.1':
dependencies:
- '@azimutt/models': 0.1.13
+ '@azimutt/models': 0.1.14
'@azimutt/utils': 0.1.5
couchbase: 4.3.1
transitivePeerDependencies:
- encoding
- supports-color
- '@azimutt/connector-mariadb@0.1.4':
+ '@azimutt/connector-mariadb@0.1.5':
dependencies:
- '@azimutt/models': 0.1.13
+ '@azimutt/models': 0.1.14
'@azimutt/utils': 0.1.5
mariadb: 3.3.1
transitivePeerDependencies:
- encoding
- '@azimutt/connector-mongodb@0.1.2(socks@2.8.3)':
+ '@azimutt/connector-mongodb@0.1.3(socks@2.8.3)':
dependencies:
- '@azimutt/models': 0.1.13
+ '@azimutt/models': 0.1.14
'@azimutt/utils': 0.1.5
mongodb: 6.8.0(socks@2.8.3)
transitivePeerDependencies:
@@ -9748,17 +9748,17 @@ snapshots:
- snappy
- socks
- '@azimutt/connector-mysql@0.1.3':
+ '@azimutt/connector-mysql@0.1.4':
dependencies:
- '@azimutt/models': 0.1.13
+ '@azimutt/models': 0.1.14
'@azimutt/utils': 0.1.5
mysql2: 3.3.5
transitivePeerDependencies:
- encoding
- '@azimutt/connector-oracle@0.1.1':
+ '@azimutt/connector-oracle@0.1.2':
dependencies:
- '@azimutt/models': 0.1.13
+ '@azimutt/models': 0.1.14
'@azimutt/utils': 0.1.5
oracledb: 6.6.0
transitivePeerDependencies:
@@ -9766,7 +9766,7 @@ snapshots:
'@azimutt/connector-postgres@0.1.9(pg-native@3.0.1)':
dependencies:
- '@azimutt/models': 0.1.13
+ '@azimutt/models': 0.1.14
'@azimutt/utils': 0.1.5
pg: 8.12.0(pg-native@3.0.1)
postgres-array: 3.0.2
@@ -9776,7 +9776,7 @@ snapshots:
'@azimutt/connector-snowflake@0.1.1(asn1.js@5.4.1)':
dependencies:
- '@azimutt/models': 0.1.13
+ '@azimutt/models': 0.1.14
'@azimutt/utils': 0.1.5
snowflake-sdk: 1.10.1(asn1.js@5.4.1)(encoding@0.1.13)
transitivePeerDependencies:
@@ -9785,16 +9785,16 @@ snapshots:
- encoding
- supports-color
- '@azimutt/connector-sqlserver@0.1.2':
+ '@azimutt/connector-sqlserver@0.1.3':
dependencies:
- '@azimutt/models': 0.1.13
+ '@azimutt/models': 0.1.14
'@azimutt/utils': 0.1.5
mssql: 11.0.1
transitivePeerDependencies:
- encoding
- supports-color
- '@azimutt/models@0.1.13':
+ '@azimutt/models@0.1.14':
dependencies:
'@azimutt/utils': 0.1.5
openai: 4.54.0(encoding@0.1.13)