0002_auth_after_sales.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import sqlalchemy as sa
  2. from alembic import op
  3. from sqlalchemy.dialects.mysql import BIGINT, BINARY, DATETIME
  4. revision = "0002_auth_after_sales"
  5. down_revision = "0001_identity_foundation"
  6. branch_labels = None
  7. depends_on = None
  8. def identity(name, primary=False):
  9. return sa.Column(name, BINARY(16), nullable=False, primary_key=primary)
  10. def number(name, primary=False):
  11. return sa.Column(name, BIGINT(unsigned=True), nullable=False, primary_key=primary)
  12. def varchar(name, length, nullable=False, primary=False, collation=None):
  13. return sa.Column(
  14. name, sa.String(length, collation=collation), nullable=nullable, primary_key=primary
  15. )
  16. def fk(local, remote):
  17. return sa.ForeignKeyConstraint(local, remote)
  18. def upgrade():
  19. op.create_table(
  20. "principals",
  21. identity("id", True),
  22. identity("tenant_id"),
  23. varchar("username", 128, collation="utf8mb4_bin"),
  24. varchar("password_hash", 255),
  25. varchar("kind", 16),
  26. varchar("status", 16),
  27. sa.UniqueConstraint("tenant_id", "id", name="uq_principals_tenant_id_id"),
  28. sa.UniqueConstraint("tenant_id", "username", name="uq_principals_tenant_username"),
  29. fk(["tenant_id"], ["tenants.id"]),
  30. sa.CheckConstraint(
  31. "kind IN ('CUSTOMER', 'STAFF', 'SERVICE')", name=op.f("ck_principals_kind")
  32. ),
  33. sa.CheckConstraint("status IN ('ACTIVE', 'SUSPENDED')", name=op.f("ck_principals_status")),
  34. )
  35. op.create_table(
  36. "access_sessions",
  37. sa.Column("token_hash", BINARY(32), primary_key=True),
  38. identity("tenant_id"),
  39. identity("principal_id"),
  40. sa.Column("expires_at", DATETIME(fsp=6), nullable=False),
  41. sa.Column("revoked", sa.Boolean(), nullable=False),
  42. fk(["tenant_id", "principal_id"], ["principals.tenant_id", "principals.id"]),
  43. )
  44. op.create_table(
  45. "store_grants",
  46. identity("tenant_id", True),
  47. identity("principal_id", True),
  48. identity("store_id", True),
  49. varchar("permission", 64, primary=True),
  50. fk(["tenant_id", "principal_id"], ["principals.tenant_id", "principals.id"]),
  51. fk(["tenant_id", "store_id"], ["stores.tenant_id", "stores.id"]),
  52. )
  53. op.create_table(
  54. "order_projections",
  55. identity("id", True),
  56. identity("tenant_id"),
  57. identity("store_id"),
  58. identity("subject_id"),
  59. varchar("source_system", 64),
  60. number("source_version"),
  61. varchar("item_id", 128),
  62. varchar("product_type", 64),
  63. sa.Column("available_quantity", sa.Integer(), nullable=False),
  64. sa.Column("signed_at", DATETIME(fsp=6), nullable=True),
  65. sa.Column("observed_at", DATETIME(fsp=6), nullable=False),
  66. sa.UniqueConstraint("tenant_id", "id", name="uq_order_projections_tenant_id_id"),
  67. fk(["tenant_id", "store_id"], ["stores.tenant_id", "stores.id"]),
  68. fk(["tenant_id", "subject_id"], ["principals.tenant_id", "principals.id"]),
  69. sa.CheckConstraint("available_quantity >= 0", name=op.f("ck_order_projections_quantity")),
  70. )
  71. op.create_table(
  72. "exchange_rules",
  73. identity("tenant_id", True),
  74. identity("store_id", True),
  75. number("version", True),
  76. sa.Column("window_days", sa.Integer(), nullable=False),
  77. sa.Column("product_types", sa.JSON(), nullable=False),
  78. varchar("citation", 2000),
  79. sa.Column("revoked", sa.Boolean(), nullable=False),
  80. fk(["tenant_id", "store_id"], ["stores.tenant_id", "stores.id"]),
  81. sa.CheckConstraint("window_days > 0", name=op.f("ck_exchange_rules_window")),
  82. )
  83. op.create_table(
  84. "tickets",
  85. identity("id", True),
  86. identity("tenant_id"),
  87. identity("store_id"),
  88. identity("subject_id"),
  89. identity("order_id"),
  90. identity("created_by"),
  91. varchar("status", 32),
  92. number("version"),
  93. number("proposal_version"),
  94. sa.Column("created_at", DATETIME(fsp=6), nullable=False),
  95. sa.Column("updated_at", DATETIME(fsp=6), nullable=False),
  96. sa.UniqueConstraint("tenant_id", "id", name="uq_tickets_tenant_id_id"),
  97. fk(["tenant_id", "store_id"], ["stores.tenant_id", "stores.id"]),
  98. fk(["tenant_id", "subject_id"], ["principals.tenant_id", "principals.id"]),
  99. fk(["tenant_id", "order_id"], ["order_projections.tenant_id", "order_projections.id"]),
  100. sa.CheckConstraint(
  101. "status IN ('DRAFT','WAITING_REVIEW','APPROVED','REJECTED','CANCELLED')",
  102. name=op.f("ck_tickets_status"),
  103. ),
  104. )
  105. op.create_index(
  106. "ix_tickets_tenant_subject_created",
  107. "tickets",
  108. ["tenant_id", "subject_id", "created_at", "id"],
  109. )
  110. op.create_table(
  111. "proposal_versions",
  112. identity("tenant_id", True),
  113. identity("proposal_id", True),
  114. number("version", True),
  115. sa.Column("quantity", sa.Integer(), nullable=False),
  116. varchar("reason", 2000),
  117. number("rule_version"),
  118. number("order_version"),
  119. varchar("citation", 2000),
  120. fk(["tenant_id", "proposal_id"], ["tickets.tenant_id", "tickets.id"]),
  121. sa.CheckConstraint("quantity > 0", name=op.f("ck_proposal_versions_quantity")),
  122. )
  123. proposal_fk = [
  124. "proposal_versions.tenant_id",
  125. "proposal_versions.proposal_id",
  126. "proposal_versions.version",
  127. ]
  128. op.create_table(
  129. "confirmations",
  130. identity("tenant_id", True),
  131. identity("proposal_id", True),
  132. number("proposal_version", True),
  133. identity("subject_id"),
  134. sa.Column("confirmed_at", DATETIME(fsp=6), nullable=False),
  135. fk(["tenant_id", "proposal_id", "proposal_version"], proposal_fk),
  136. )
  137. op.create_table(
  138. "approvals",
  139. identity("id", True),
  140. identity("tenant_id"),
  141. identity("proposal_id"),
  142. number("proposal_version"),
  143. identity("submitted_by"),
  144. varchar("status", 32),
  145. sa.Column("decided_by", BINARY(16), nullable=True),
  146. varchar("reason", 2000, nullable=True),
  147. sa.UniqueConstraint(
  148. "tenant_id", "proposal_id", "proposal_version", name="uq_approvals_proposal"
  149. ),
  150. fk(["tenant_id", "proposal_id", "proposal_version"], proposal_fk),
  151. )
  152. op.create_table(
  153. "command_receipts",
  154. identity("tenant_id", True),
  155. identity("actor_id", True),
  156. varchar("route", 128, primary=True),
  157. varchar("key", 64, primary=True, collation="utf8mb4_bin"),
  158. sa.Column("request_hash", BINARY(32), nullable=False),
  159. sa.Column("response", sa.JSON(), nullable=False),
  160. )
  161. op.create_table(
  162. "business_events",
  163. identity("id", True),
  164. identity("tenant_id"),
  165. identity("actor_id"),
  166. identity("aggregate_id"),
  167. number("aggregate_version"),
  168. varchar("event_type", 64),
  169. varchar("request_id", 36),
  170. sa.Column("payload", sa.JSON(), nullable=False),
  171. sa.Column("occurred_at", DATETIME(fsp=6), nullable=False),
  172. varchar("delivery_status", 16),
  173. )
  174. op.create_index(
  175. "ix_business_events_delivery", "business_events", ["delivery_status", "occurred_at", "id"]
  176. )
  177. def downgrade():
  178. for name in (
  179. "business_events",
  180. "command_receipts",
  181. "approvals",
  182. "confirmations",
  183. "proposal_versions",
  184. "tickets",
  185. "exchange_rules",
  186. "order_projections",
  187. "store_grants",
  188. "access_sessions",
  189. "principals",
  190. ):
  191. op.drop_table(name)