diff --git a/ERD.md b/ERD.md index ab78d86..362f0ec 100644 --- a/ERD.md +++ b/ERD.md @@ -2,14 +2,14 @@ > Generated by [`prisma-markdown`](https://github.com/samchon/prisma-markdown) - [Articles](#Articles) -- [Inquiries](#Inquiries) - [Systematic](#Systematic) -- [Sales](#Sales) - [Actors](#Actors) -- [Coins](#Coins) -- [Orders](#Orders) +- [Sales](#Sales) - [Carts](#Carts) +- [Orders](#Orders) - [Coupons](#Coupons) +- [Coins](#Coins) +- [Inquiries](#Inquiries) ## Articles ```mermaid @@ -230,223 +230,6 @@ relationship between [bbs_article_comment_snapshots](#bbs_article_comment_snapsh > Sequence order of the attached file in the belonged snapshot. -## Inquiries -```mermaid -erDiagram -"bbs_articles" { - String id PK - DateTime created_at - DateTime deleted_at "nullable" -} -"bbs_article_snapshots" { - String id PK - String bbs_article_id FK - String format - String title - String body - DateTime created_at -} -"bbs_article_comments" { - String id PK - String bbs_article_id FK - String parent_id FK "nullable" - DateTime created_at - DateTime deleted_at "nullable" -} -"shopping_sale_snapshots" { - String id PK - String shopping_sale_id FK - DateTime created_at -} -"shopping_sale_snapshot_inquiries" { - String id PK - String shopping_sale_id FK - String shopping_sale_snapshot_id FK - String shopping_customer_id FK - String type - DateTime created_at - DateTime read_by_seller_at "nullable" -} -"shopping_sale_snapshot_questions" { - String id PK - Boolean secret -} -"shopping_sale_snapshot_reviews" { - String id PK - String shopping_order_good_id FK -} -"shopping_sale_snapshot_review_snapshots" { - String id PK - Float score -} -"shopping_sale_snapshot_inquiry_answers" { - String id PK - String shopping_sale_snapshot_inquiry_id FK - String shopping_seller_customer_id FK -} -"shopping_sale_snapshot_inquiry_comments" { - String id PK - String shopping_customer_id FK - String actor_type -} -"bbs_article_snapshots" }|--|| "bbs_articles" : article -"bbs_article_comments" }|--|| "bbs_articles" : article -"bbs_article_comments" }o--o| "bbs_article_comments" : parent -"shopping_sale_snapshot_inquiries" ||--|| "bbs_articles" : base -"shopping_sale_snapshot_inquiries" }|--|| "shopping_sale_snapshots" : snapshot -"shopping_sale_snapshot_questions" |o--|| "shopping_sale_snapshot_inquiries" : base -"shopping_sale_snapshot_reviews" |o--|| "shopping_sale_snapshot_inquiries" : base -"shopping_sale_snapshot_review_snapshots" ||--|| "bbs_article_snapshots" : base -"shopping_sale_snapshot_inquiry_answers" ||--|| "bbs_articles" : base -"shopping_sale_snapshot_inquiry_answers" |o--|| "shopping_sale_snapshot_inquiries" : inquiry -"shopping_sale_snapshot_inquiry_comments" ||--|| "bbs_article_comments" : base -``` - -### `shopping_sale_snapshot_inquiries` -Inquiry about a sale snapshot. - -`shopping_sale_snapshot_inquiries` is a subtype entity of -[bbs_articles](#bbs_articles), and represents inquiries written by -[customers](#shopping_customers) about a [sale](#shopping_sales) -registered by the [seller](#shopping_sellers) (however, to trace the -exact [snapshot](#shopping_sale_snapshots), it is referencing not -sale but snapshot). - -In addition, since the customer is waiting for the seller's response after -writing the inquiry, whether the seller has viewed the inquiry written by -the customer is provided for reference as `read_by_seller_at` property. -Of course, since the inquiry itself is a subtype of a article, it is also -possible for sellers to communicate with each other through -[comments](#shopping_sale_snapshot_inquiry_comments) before an -[official response](#shopping_sale_snapshot_inquiry_responses). - -However, comments themselves can be made by every customers, even if they -are not the person who wrote the article. Of course, it cannot be written -unless the seller is a party. - -**Properties** - - `id`: PK + FK. - - `shopping_sale_id` - > Belonged sale's [shopping_sales.id](#shopping_sales) - > - > Duplicated property for fast sorting. - - `shopping_sale_snapshot_id`: Belonged snapshot's [shopping_sale_snapshots.id](#shopping_sale_snapshots) - - `shopping_customer_id`: Writer customer's [shopping_customers.id](#shopping_customers) - - `type` - > Type of the inquiry article. - > - > - `question` - > - `review` - - `created_at` - > Creation time of record. - > - > Duplicated property for fast sorting. - - `read_by_seller_at`: The first time when the seller read the inquiry. - -### `shopping_sale_snapshot_questions` -Question about sale snapshot. - -`shopping_sale_snapshot_questions` is a subtype entity of -[shopping_sale_snapshot_inquiries](#shopping_sale_snapshot_inquiries), and is used when a -[customer](#shopping_customers) wants to ask something about a -sale ([snapshot](#shopping_sale_snapshots) at the time) registered by -the [seller](#shopping_sellers). - -And, like most shopping malls, `shopping_sale_snapshot_questions` also -provides a `secret` attribute, allowing you to create a "secret message" -that can only be viewed by the seller and the customer who wrote the -question. - -**Properties** - - `id`: PK + FK. - - `secret` - > Whether secret or not. - > - > If secret article, only the writer customer and related seller can see - > the detailed content. - -### `shopping_sale_snapshot_reviews` -Reviews for sale snapshots. - -`shopping_sale_snapshot_reviews` is a subtype entity of -[shopping_sale_snapshot_inquiries](#shopping_sale_snapshot_inquiries), and is used when a -[customer](#shopping_customers) purchases a sale -([snapshot](#shopping_sale_snapshots) at the time) registered by the -[seller](#shopping_sellers) as a product and leaves a review and -rating for it. - -For reference, `shopping_sale_snapshot_reviews` and -[shopping_order_goods](#shopping_order_goods) have a logarithmic relationship of N: 1, -but this does not mean that customers can continue to write reviews for -the same product indefinitely. Wouldn't there be restrictions, such as -if you write a review once, you can write an additional review a month -later? - -**Properties** - - `id`: PK + FK. - - `shopping_order_good_id`: Belonged good's [shopping_order_goods.id](#shopping_order_goods) - -### `shopping_sale_snapshot_review_snapshots` -A snapshot of the content of the review for the sale snapshot. - -`shopping_sale_snapshot_review_snapshots` is a subtype entity of -[bbs_article_snapshots](#bbs_article_snapshots) and is designed to add a `score` property -to the content of [review article](#shopping_sale_snapshot_reviews). - -In other words, after writing a review article, customers can edit it -and change the evaluation `score` at any time. - -**Properties** - - `id`: PK + FK. - - `score`: Estimation score value. - -### `shopping_sale_snapshot_inquiry_answers` -Answers to questions about sale snapshots. - -`shopping_sale_snapshot_inquiry_answers` is an entity that embodies the -official answer written by the [seller](#shopping_sellers) to the -[inquiry](#shopping_sale_snapshot_inquiries) written by the -[customer](#shopping_customers). - -Of course, in addition to writing an official response like this, it is -also possible for the seller to communicate with the questioner and -multiple customers through -[comments](#shopping_sale_snapshot_inquiry_comments) in the -attribution inquiry. - -For refererence, it is not possible to write comments on this answer. -Encourage people to write comments on the inquiry article. This is to -prevent comments from being scattered in both inquiry and response -articles. - -**Properties** - - `id`: PK + FK - - `shopping_sale_snapshot_inquiry_id`: Belonged inquiry's [shopping_sale_snapshot_inquiries.id](#shopping_sale_snapshot_inquiries) - - `shopping_seller_customer_id`: Answered seller's [shopping_customers.id](#shopping_customers) - -### `shopping_sale_snapshot_inquiry_comments` -A comment written on an inquiry article. - -`shopping_sale_snapshot_inquiry_comments` is a subtype entity of -[bbs_article_comments](#bbs_article_comments), and is used when you want to communicate with -multiple people about an [inquiry](#shopping_sale_snapshot_inquiries) -written by a [customer](#shopping_customers). - -For reference, only related parties can write comments for -[sellers](#shopping_sellers), but there is no limit to customers. -In other words, anyone customer can freely write a comment, even if they are -not the person who wrote the inquiry. - -**Properties** - - `id`: PK + FK - - `shopping_customer_id`: Writer's [shopping_customers.id](#shopping_customers) - - `actor_type` - > Type of the writer. - > - > - customer - > - seller - - ## Systematic ```mermaid erDiagram @@ -600,1083 +383,1023 @@ just use only one. This concept is designed to be expandable in the future. - `deleted_at`: Deletion time of record. -## Sales +## Actors ```mermaid erDiagram -"shopping_channels" { +"shopping_customers" { String id PK - String code UK - String name UK - Boolean exclusive + String shopping_channel_id FK + String shopping_member_id FK "nullable" + String shopping_external_user_id FK "nullable" + String shopping_citizen_id FK "nullable" + String href + String referrer + String ip DateTime created_at - DateTime updated_at - DateTime deleted_at "nullable" } -"shopping_channel_categories" { +"shopping_external_users" { String id PK - String shopping_channel_id FK - String parent_id FK "nullable" - String name + String application + String uid + String nickname + String data "nullable" + String password DateTime created_at - DateTime updated_at - DateTime deleted_at "nullable" } -"shopping_sections" { +"shopping_citizens" { String id PK - String code UK - String name UK + String shopping_channel_id FK "nullable" + String mobile + String name DateTime created_at - DateTime updated_at DateTime deleted_at "nullable" } -"shopping_sales" { - String id PK - String shopping_section_id FK - String shopping_seller_customer_id FK - DateTime created_at - DateTime opened_at "nullable" - DateTime closed_at "nullable" - DateTime paused_at "nullable" - DateTime suspended_at "nullable" -} -"shopping_sale_snapshots" { +"shopping_members" { String id PK - String shopping_sale_id FK + String shopping_channel_id FK + String shopping_citizen_id FK "nullable" + String nickname + String password DateTime created_at + DateTime updated_at + DateTime withdrawn_at "nullable" } -"shopping_sale_snapshot_channels" { +"shopping_member_emails" { String id PK - String shopping_sale_snapshot_id FK String shopping_channel_id FK + String shopping_member_id FK + String value + DateTime created_at } -"shopping_sale_snapshot_channel_categories" { - String id PK - String shopping_sale_snapshot_channel_id FK - String shopping_channel_category_id FK -} -"shopping_sale_snapshot_units" { - String id PK - String shopping_sale_snapshot_id FK - String name - Boolean primary - Boolean required - Int sequence -} -"shopping_sale_snapshot_unit_options" { +"shopping_sellers" { String id PK - String shopping_sale_snapshot_unit_id FK - String name - String type - Boolean variable - Int sequence + String shopping_member_id FK + DateTime created_at + DateTime deleted_at "nullable" } -"shopping_sale_snapshot_unit_option_candidates" { +"shopping_administrators" { String id PK - String shopping_sale_snapshot_unit_option_id FK - String name - Int sequence + String shopping_member_id UK + DateTime created_at + DateTime deleted_at "nullable" } -"shopping_sale_snapshot_unit_stocks" { +"shopping_addresses" { String id PK - String shopping_sale_snapshot_unit_id FK + String mobile String name - Float nominal_price - Float real_price - Int quantity - Int sequence -} -"shopping_sale_snapshot_unit_stock_choices" { - String id PK - String shopping_sale_snapshot_unit_stock_id FK - String shopping_sale_snapshot_unit_option_candidate_id FK - Int sequence + String country + String province + String city + String department + String possession + String zip_code + String special_note "nullable" + DateTime created_at } -"shopping_channel_categories" }|--|| "shopping_channels" : channel -"shopping_channel_categories" }o--o| "shopping_channel_categories" : parent -"shopping_sales" }|--|| "shopping_sections" : section -"shopping_sale_snapshots" }|--|| "shopping_sales" : sale -"shopping_sale_snapshot_channels" }|--|| "shopping_sale_snapshots" : snapshot -"shopping_sale_snapshot_channels" }|--|| "shopping_channels" : channel -"shopping_sale_snapshot_channel_categories" }|--|| "shopping_sale_snapshot_channels" : to_channel -"shopping_sale_snapshot_channel_categories" }|--|| "shopping_channel_categories" : category -"shopping_sale_snapshot_units" }|--|| "shopping_sale_snapshots" : snapshot -"shopping_sale_snapshot_unit_options" }|--|| "shopping_sale_snapshot_units" : unit -"shopping_sale_snapshot_unit_option_candidates" }|--|| "shopping_sale_snapshot_unit_options" : option -"shopping_sale_snapshot_unit_stocks" }|--|| "shopping_sale_snapshot_units" : unit -"shopping_sale_snapshot_unit_stock_choices" }|--|| "shopping_sale_snapshot_unit_stocks" : stock -"shopping_sale_snapshot_unit_stock_choices" }|--|| "shopping_sale_snapshot_unit_option_candidates" : candidate +"shopping_customers" }o--|| "shopping_members" : member +"shopping_customers" }o--|| "shopping_external_users" : external_user +"shopping_customers" }o--|| "shopping_citizens" : citizen +"shopping_members" }o--|| "shopping_citizens" : citizen +"shopping_member_emails" }|--|| "shopping_members" : member +"shopping_sellers" |o--|| "shopping_members" : member +"shopping_administrators" |o--|| "shopping_members" : member ``` -### `shopping_sales` -Seller **sales** products. +### `shopping_customers` +Customer information, but not a person but a **connection** basis. -`shopping_sales` is an entity that embodies "product sales" (sales) -information registered by the [seller](#shopping_sellers). And the -main information of the sale is recorded in the sub -[shopping_sale_snapshots](#shopping_sale_snapshots), not in the main `shopping_sales`. -When a seller changes a previously registered item, the existing -`shopping_sales` record is not changed, but a new snapshot record is -created. +`shopping_customers` is an entity that literally embodies the information +of those who participated in the market as customers. By the way, the +`shopping_Customers` does not mean a person, but a **connection** basis. +Therefore, even if the same person connects to the shopping mall multiple, +multiple records are created in `shopping_customers`. -This is to preserve the [customer](#shopping_customers)'s -[purchase history](#shopping_orders) flawlessly after the customer -purchases a specific item, even if the seller changes the components or -price of the item. It is also intended to support sellers in so-called -A/B testing, which involves changing components or prices and measuring -the performance in each case. +The first purpose of this is to track the customer's inflow path in detail, and +it is for cases where the same person enters as a non-member, puts items in the +[shopping cart](#shopping_cart_commodities) in advance, and only authenticates +their real name or registers/logs in at the moment of +[payment](#shopping_order_publishes). It is the second. Lastly, it is +to accurately track the activities that a person performs at the +shopping mall in various ways like below. + +- Same person comes from an [external service](#shopping_external_users) +- Same person creates multiple [accounts](#shopping_members) +- Same person makes a purchase as a non-member with only [real name authentication](#shopping_citizens) +- Same person acts both [seller](#shopping_sellers) and [admin](#shopping_administrators) at the same time + +Therefore, `shopping_customers` can have multiple records with the same +[shopping_citizens](#shopping_citizens), [shopping_members](#shopping_members), and +[shopping_external_users](#shopping_external_users). Additionally, if a customer signs up for +membership after verifying their real name or signs up for our service +after being a user of an external service, all related records are changed +at once. Therefore, identification and tracking of customers can be done +very systematically. **Properties** - `id`: Primary Key. - - `shopping_section_id`: Belonged section's [shopping_sections.id](#shopping_sections) - - `shopping_seller_customer_id`: Belonged seller's [shopping_customers.id](#shopping_customers) - - `created_at` - > Creation time of record. - > - > Note that, this property is different with `opened_at`, which means - > the opening time of sale. - - `opened_at` - > Opening time of sale. - > - > If `null` value assigned, it means not opened yet. - - `closed_at` - > Closing time of sale. - > - > If `null` value assigned, the sale is forever. - - `paused_at` - > Paused time. - > - > The time when seller paused the sale in some reason. - > - > [Customers](#shopping_customers) still can see the sale in list - > and detail page, but a label "The sale is paused for a while by seller" - > be attached. - - `suspended_at` - > Suspended time. + - `shopping_channel_id`: Belonged channel's [shopping_channels.id](#shopping_channels) + - `shopping_member_id`: Belonged member's [shopping_members.id](#shopping_members) + - `shopping_external_user_id`: Belonged external service user's [shopping_external_users.id](#shopping_external_users) + - `shopping_citizen_id`: Belonged citizen's [shopping_citizens.id](#shopping_citizens) + - `href` + > Connection URL. > - > The time when seller suspended the sale in some reason. + > [window.location.href](#window) + - `referrer` + > Referrer URL. > - > [Customers](#shopping_customers) can't see the sale in list and - > detail page. It seems almost ssame with soft deletion, but there're - > some differences. + > [window.document.referrer](#window) + - `ip`: IP address, + - `created_at` + > Creation time of record. > - > At 1st, seller and [administrator](#shopping_administrators) can - > see suspended sale in list and detail page. At 2nd, seller can - > resume the sale at any time. + > It means the time when the customer connected to the shopping mall. -### `shopping_sale_snapshots` -Sale snapshot information. +### `shopping_external_users` +External user information. -`shopping_sale_snapshots` is an entity representing snapshot record of -belonged [sale](#shopping_sales). The snapshot record is created -whenever the seller newly creates or updates the sale. +`shopping_external_users` is an entity dsigned for when this system needs +to connect with external services and welcome their users as customers of +this service. -Sale | Cart | Order ------|------|------ -x | [shopping_carts](#shopping_carts) | [shopping_orders](#shopping_orders) -[shopping_sale_snapshots](#shopping_sale_snapshots) | [shopping_cart_commodities](#shopping_cart_commodities) | [shopping_order_goods](#shopping_order_goods) -[shopping_sale_snapshot_unit_stocks](#shopping_sale_snapshot_unit_stocks) | [shopping_cart_commodity_stocks](#shopping_cart_commodity_stocks) | x +For reference, customers who connect from an external service must have +this record, and the external service user is identified through the two +attributes `application` and `uid`. If a customer connected from an +external service completes [real-name authentication](#shopping_citizens) +from this service, each time the external service user reconnects to this +service and issues a new [customer](#shopping_customers) authentication +token, [real-name authentication](#shopping_citizens) begins with +completed. + +And `password` is the password issued to the user by the external service +system (the so-called permanent user authentication token), and is never +the actual user password. However, for customers who entered the same +`application` and `uid` as the current external system user, this is to +determine whether to view this as a correct external system user or a +violation. + +In addition, additional information received from external services can +be recorded in the `data` field in JSON format. **Properties** - `id`: Primary Key. - - `shopping_sale_id`: Belonged sale's [shopping_sales.id](#shopping_sales) + - `application` + > Identifier code of the external service. + > + > It can be same with [shopping_channels.code](#shopping_channels) in common. + - `uid`: Identifier key of external user from the external system. + - `nickname`: Nickname of external user in the external system. + - `data`: Additional information about external user from the external system. + - `password` + > Password of external user from the external system. + > + > This is a password issued to the user by an external service, and is + > by no means the actual user password. However, for customers who + > entered the same application and code as the current external system + > user, this is to determine whether to view this as a correct external + > system user or a violation. - `created_at` > Creation time of record. > - > It means the time when the seller created or updated the sale. + > Another word, first time when the external user connected. -### `shopping_sale_snapshot_contents` -Content information of sale snapshot. +### `shopping_citizens` +Citizen verification information. -`shopping_sale_snapshot_contents` is an entity embodies the body contents -of [sale snapshot](#shopping_sale_snapshots). Also, it contains -revert policy of the sale. +`shopping_citizens` is an entity that records the user's real name and +mobile input information. -**Properties** - - `id`: Primary Key. - - `shopping_sale_snapshot_id`: Belonged snapshot's [shopping_sale_snapshots.id](#shopping_sale_snapshots) - - `title`: Title of the content. - - `format` - > Format of the body content. - > - > Same meaning with extension like `html`, `md`, `txt`. - - `body`: The main body content. - - `revert_policy` - > Revert policy. - > - > This is essential in South Korea, but I don't know well in overseas. - > - > Just use when you need. - -### `shopping_sale_snapshot_content_files` -Attachment file of sale snapshot content. - -**Properties** - - `id`: - - `shopping_sale_snapshot_content_id`: - - `attachment_file_id`: - - `sequence`: +For reference, in South Korea, real name authentication is required for +e-commerce participants, so the `name` attribute is important. However, +the situation is different overseas, so in reality, `mobile` attributes +are the most important, and identification of individual users is also +done based on this mobile. -### `shopping_sale_snapshot_tags` -Search tag of sale snapshot. +Of course, real name and mobile phone authentication information are +encrypted and stored. **Properties** - - `id`: - - `shopping_sale_snapshot_id`: - - `value`: - - `sequence`: - -### `shopping_sale_snapshot_channels` -Target channel of sale snapshot to sell. + - `id`: Primary Key. + - `shopping_channel_id` + > Belonged channel's [shopping_channels.id](#shopping_channels) + > + > This is to manage personal information separately for each channel, + > and also to recognize cases where the same citizen is authenticated + > through different channels. + - `mobile`: Mobile phone number. + - `name`: Real name, or equivalent name identifiable. + - `created_at` + > Creation time of record. + > + > In other words, the 1st time of citizen activation. + - `deleted_at`: Deletion time of record. -`shopping_sale_snapshot_channels` is an entity that expresses through -which [channel](#shopping_channels) a listing -[snapshot](#shopping_sale_snapshots) is sold, and is designed to -resolve the M:N relationship between two tables. +### `shopping_members` +Member Account. -**Properties** - - `id`: Primary Key. - - `shopping_sale_snapshot_id`: Belonged snapshot's [shopping_sale_snapshots.id](#shopping_sale_snapshots). - - `shopping_channel_id`: Belonged channel's [shopping_channels.id](#shopping_channels). +`shopping_members` is an entity that symbolizes the case when a user +signs up as a member of this system. -### `shopping_sale_snapshot_channel_categories` -Category classification info of sale snapshot. +In addition, `shopping_members` itself is a supertype entity, forming +and managing subtypes for various types of members. However, +[shopping_customers](#shopping_customers) are an exception, and due to the nature of +their records being created on a per-connection basis, they are not +divided into separate subtype entities when they sign up for membership. -`shopping_sale_snapshot_channel_categories` is an entity that expresses -which [category](#shopping_channel_categories) the listing -[snapshot](#shopping_sale_snapshots) is classified into in each -[channel](#shopping_channels). +For reference, `shopping_members` allows multiple subtypes. Therefore, +it is also possible for a [citizen](#shopping_citizens) to be sometimes +a [customer](#shopping_customers), sometimes a +[seller](#shopping_sellers), sometimes an +[administrator](#shopping_administrators), and so on. -It is designed to resolve the M:N relationship between -[shopping_sale_snapshots](#shopping_sale_snapshots) and [shopping_channel_categories](#shopping_channel_categories), -respectively. Of course, if the target category being referred to is a -major category, all minor categories belonging to it can also be used. +Of course, this is according to system theory, and it is unclear what +the planning will be like. **Properties** - `id`: Primary Key. - - `shopping_sale_snapshot_channel_id`: Belonged assigned channel of sale snapshot's [shopping_sale_snapshot_channels.id](#shopping_sale_snapshot_channels) - - `shopping_channel_category_id`: Belonged channel category's [shopping_channel_categories.id](#shopping_channel_categories) + - `shopping_channel_id`: Belonged channel's [shopping_channels.id](#shopping_channels) + - `shopping_citizen_id`: Belonged citizen's [shopping_citizens.id](#shopping_citizens) + - `nickname`: Nickname. + - `password`: Password for log-in. + - `created_at` + > Creation time of record. + > + > In other words, the joining time. + - `updated_at`: Update time of record. + - `withdrawn_at`: Deletion time of record. -### `shopping_sale_snapshot_units` -Product composition information handled in the sale snapshot. +### `shopping_member_emails` +Email address of member. -`shopping_sale_snapshot_units` is an entity that embodies the -"individual product" information handled in the -[sale snapshot](#shopping_sale_snapshots). +This system allows multiple email addresses to be registered for one +[member](#shopping_members). If you don't have to plan such multiple +email addresses, just use only one. -For reference, the reason why `shopping_sale_snapshot_units` is separated -from [shopping_sale_snapshots](#shopping_sale_snapshots) by an algebraic relationship of -1: N is because there are some cases where multiple products are sold -in one listing. This is the case with so-called "bundled products". +**Properties** + - `id`: + - `shopping_channel_id` + > Belonged channel's [shopping_channels.id](#shopping_channels) + > + > Duplicated attribute with [shopping_members.channel_id](#shopping_members), but + > just denormalized for composing unique constraint. + - `shopping_member_id`: Belonged member's [shopping_members.id](#shopping_members) + - `value`: Email address. + - `created_at`: Creation time of record. -- Bundle from regular product (Macbook Set) -- main body -- keyboard -- mouse -- Apple Care (Free A/S Voucher) +### `shopping_sellers` +Seller information. -And again, `shopping_sale_snapshot_units` does not in itself refer to -the final [stock](#shopping_sale_snapshot_unit_stocks) that the -customer will purchase. -The [final stock](#shopping_sale_snapshot_unit_stocks) can be -found only after selecting all given -[options](#shopping_sale_snapshot_unit_options) and their -[candidate](#shopping_sale_snapshot_unit_option_candidates) values. +`shopping_sellers` is an entity that embodies a person who registers +[sales](#shoppingsales) to operate selling activities, with +[membership](#shoppingmembers) joining. -For example, even if you buy a Macbook, the -[final stocks](#shopping_sale_snapshot_unit_stocks) are determined -only after selecting all the -[options](#shopping_sale_snapshot_unit_options) (CPU / RAM / SSD), etc. +For reference, unlike [customers](#shopping_customers) which can +participate even without [membership](#shopping_members) joining, +seller must join membership to operate sales. Also, seller must +do the [real-name authentication](#shopping_citizens), too. **Properties** - `id`: Primary Key. - - `shopping_sale_snapshot_id`: Belonged snapshot's [shopping_sale_snapshots.id](#shopping_sale_snapshots) - - `name`: Representative name of the unit. - - `primary` - > Whether the unit is primary or not. - > - > Just a labeling value. - - `required` - > Whether the unit is required or not. + - `shopping_member_id`: Belonged member's [shopping_members.id](#shopping_members). + - `created_at` + > Creation time of record. > - > When the unit is required, the customer must select the unit. If do - > not select, customer can't buy it. + > Joining time as seller, and it can be different with + > [membership](#shopping_members) joining time. + - `deleted_at` + > Withdrawal time. > - > For example, if there's a sale "Macbook Set" and one of the unit is - > the "Main Body", is it possible to buy the "Macbook Set" without the - > "Main Body" unit? This property is for that case. - - `sequence`: Sequence order in belonged snapshot. - -### `shopping_sale_snapshot_unit_options` -Individual option information on units for sale. - -`shopping_sale_snapshot_unit_options` is a subsidiary entity of -[shopping_sale_snapshot_units](#shopping_sale_snapshot_units) that represents individual products -in the sale, and is an entity designed to represent individual option -information for the unit. + > It can be different with [shopping_members.deleted_at](#shopping_members). -- Examples of Options -- optional options -- Computer: CPU, RAM, SSD, etc. -- Clothes: size, color, style, etc. -- descriptive options -- Engrave -- Simple question +### `shopping_administrators` +Administrator account. -If the `type` of option is a `variable` value in `"select"`, the -[final stock](#shopping_sale_snapshot_unit_stocks) that the customer -will purchase changes depending on the selection of the -[candidate](#shopping_sale_snapshot_unit_option_candidates) value. +`shopping_administrators` is an entity that embodies a person who manages +the shopping mall system, with [membership](#shopping_members) joining. -Conversely, if it is a `type` other than `"select"`, or if the `type` -is `"select"` but `variable` is `false`, this is an option that has no -meaning beyond simple information transfer. Therefore, no matter what -value the customer enters and chooses when purchasing it, the option in -this case does not affect the -[final stock](#shopping_sale_snapshot_unit_stocks). +For reference, unlike [customers](#shopping_customers) which can +participate even without [membership](#shopping_members) joining, +administrator must join membership to operate sales. Also, administrator must +do the [real-name authentication](#shopping_citizens), too. **Properties** - `id`: Primary Key. - - `shopping_sale_snapshot_unit_id`: Belonged unit's [shopping_sale_snapshot_units.id](#shopping_sale_snapshot_units) - - `name`: Name of the option. - - `type` - > Type of the option. - > - > - `select`: The way selecting one of the candidate values. - > - `boolean` - > - `number` - > - `string` - - `variable` - > Whether the option is variable or not. + - `shopping_member_id`: Belonged member's [shopping_members.id](#shopping_members). + - `created_at` + > Creation time of record. > - > When `type` of current option is `"select"`, this attribute means - > whether selecting different - > [candidate](#shopping_sale_snapshot_unit_option_candidate) value - > affects the [final stock](#shopping_sale_snapshot_unit_stocks) - > or not. + > Joining time as an administrator, and can be different with + > [membership](#shopping_members) joining time. + - `deleted_at` + > Deletion time of record. > - > For reference, if `type` value is not `"select"`, this attribute - > is always `false`. - - `sequence`: Sequence order in belonged unit. - -### `shopping_sale_snapshot_unit_option_candidates` -Selectable candidate values within an option. - -`shopping_sale_snapshot_unit_option_candidates` is an entity that -represents individual candidate values that can be selected from -[options](#shopping_sale_snapshot_unit_options) of the "select" type. - -- Example -- RAM: 8GB, 16GB, 32GB -- GPU: RTX 3060, RTX 4080, TESLA -- License: Private, Commercial, Educatiion + > Withdrawal time from administrator, and can be different with + > [shopping_members.deleted_at](#shopping_members). -By the way, if belonged [option](#shopping_sale_snapshot_unit_options) -is not "select" type, this entity never being used. +### `shopping_addresses` +The address information. **Properties** - `id`: Primary Key. - - `shopping_sale_snapshot_unit_option_id`: Belonged option's [shopping_sale_snapshot_unit_options.id](#shopping_sale_snapshot_unit_options) - - `name`: Representative name of candidate value. - - `sequence`: Sequence order in option. - -### `shopping_sale_snapshot_unit_stocks` -Final component information on units for sale. - -`shopping_sale_snapshot_unit_stocks` is a subsidiary entity of -[shopping_sale_snapshot_units](#shopping_sale_snapshot_units) that represents a product catalog -for sale, and is a kind of final stock that is constructed by selecting -all [options](#shopping_sale_snapshot_unit_options) -(variable "select" type) and their -[candidate](#shopping_sale_snapshot_unit_option_candidates) values in -the belonging unit. It is the "good" itself that customers actually -purchase. - -- Product Name) MacBook -- Options -- CPU: { i3, i5, i7, i9 } -- RAM: { 8GB, 16GB, 32GB, 64GB, 96GB } -- SSD: { 256GB, 512GB, 1TB } -- Number of final stocks: 4 * 5 * 3 = 60 - -For reference, the total number of `shopping_sale_snapshot_unit_stocks` -records in an attribution unit can be obtained using Cartesian Product. -In other words, the value obtained by multiplying all the candidate -values that each (variable "select" type) option can have by the number -of cases is the total number of final stocks in the unit. - -Of course, without a single variable "select" type option, the final -stocks count in the unit is only 1. - -**Properties** - - `id`: Primary Key. - - `shopping_sale_snapshot_unit_id`: Belonged unit's [shopping_sale_snapshot_units.id](#shopping_sale_snapshot_units) - - `name`: Name of the final stock. - - `nominal_price` - > Nominal price. - > - > This is not real price to pay, but just a nominal price to show. - > If this value is greater than the `real_price`, it would be shown - > like seller is giving a discount. - - `real_price`: Real price to pay. - - `quantity` - > Initial inventory quantity. - > - > If this stock has been sold over this quantity count, the stock can't - > be sold anymore, because of out of stock. In that case, the seller can - > supplement the inventory quantity by registering some - > [shopping_sale_snapshot_unit_stock_supplements](#shopping_sale_snapshot_unit_stock_supplements) records. - - `sequence`: Sequence order in belonged unit. - -### `shopping_sale_snapshot_unit_stock_choices` -Selection information of final stock. - -`shopping_sale_snapshot_unit_stock_choices` is an entity that represents -which [option](#shopping_sale_snapshot_unit_options) of each `variable` -"select" `type` was selected for each stock and which -[candidate value](#shopping_sale_snapshot_unit_option_candidates) was -selected within it. - -Of course, if the bound [unit](#shopping_sale_snapshot_units) does not -have any [options](#shopping_sale_snapshot_unit_options), this entity -can also be ignored. - -**Properties** - - `id`: Primary Key. - - `shopping_sale_snapshot_unit_stock_id`: Belonged stock's [shopping_sale_snapshot_unit_stocks.id](#shopping_sale_snapshot_unit_stocks) - - `shopping_sale_snapshot_unit_option_candidate_id`: Belonged candidate's [shopping_sale_snapshot_unit_option_candidates.id](#shopping_sale_snapshot_unit_option_candidates) - - `sequence`: Sequence order in belonged stock. - -### `shopping_sale_snapshot_unit_stock_supplements` -Supplementation of inventory quantity of stock. - -You know what? If a stock has been sold over its -[initial inventory quantity](#shopping_sale_snapshot_unit_stocks), -the stock can't be sold anymore, because of out of stock. In that case, how the -[shopping_sellers](#shopping_sellers) should do? - -When the sotck is sold out, seller can supplement the inventory record by -registering this `shopping_sale_snapshot_unit_stock_supplements` record. Right, -this `shopping_sale_snapshot_unit_stock_supplements` is an entity that embodies -the supplementation of the inventory quantity of the belonged -[stock](#shopping_sale_snapshot_unit_stocks). - -**Properties** - - `id`: Primary Key. - - `shopping_sale_snapshot_unit_stock_id`: Belonged stock's [shopping_sale_snapshot_unit_stocks.id](#shopping_sale_snapshot_unit_stocks) - - `quantity`: Supplemented inventory quantity. - - `created_at` - > Creation time of record. + - `mobile`: Mobile number. + - `name` + > Representative name of the address. > - > When the inventory be supplemented. + > Sometimes be receiver's name, and sometimes be place name. + - `country`: Target country. + - `province`: Target province. + - `city`: Target city. + - `department`: Department name. + - `possession`: Detailed address containing room number. + - `zip_code`: Zip code, or postal code. + - `special_note`: Special description if required. + - `created_at`: Creation time of record. -## Actors +## Sales ```mermaid erDiagram -"shopping_customers" { +"shopping_sales" { String id PK - String shopping_channel_id FK - String shopping_member_id FK "nullable" - String shopping_external_user_id FK "nullable" - String shopping_citizen_id FK "nullable" - String href - String referrer - String ip + String shopping_section_id FK + String shopping_seller_customer_id FK DateTime created_at + DateTime opened_at "nullable" + DateTime closed_at "nullable" + DateTime paused_at "nullable" + DateTime suspended_at "nullable" } -"shopping_external_users" { +"shopping_sale_snapshots" { String id PK - String application - String uid - String nickname - String data "nullable" - String password + String shopping_sale_id FK DateTime created_at } -"shopping_citizens" { +"shopping_sale_snapshot_channels" { String id PK - String shopping_channel_id FK "nullable" - String mobile + String shopping_sale_snapshot_id FK + String shopping_channel_id FK +} +"shopping_sale_snapshot_channel_categories" { + String id PK + String shopping_sale_snapshot_channel_id FK + String shopping_channel_category_id FK +} +"shopping_sale_snapshot_units" { + String id PK + String shopping_sale_snapshot_id FK String name - DateTime created_at - DateTime deleted_at "nullable" + Boolean primary + Boolean required + Int sequence } -"shopping_members" { +"shopping_sale_snapshot_unit_options" { String id PK - String shopping_channel_id FK - String shopping_citizen_id FK "nullable" - String nickname - String password - DateTime created_at - DateTime updated_at - DateTime withdrawn_at "nullable" + String shopping_sale_snapshot_unit_id FK + String name + String type + Boolean variable + Int sequence } -"shopping_member_emails" { +"shopping_sale_snapshot_unit_option_candidates" { String id PK - String shopping_channel_id FK - String shopping_member_id FK - String value - DateTime created_at + String shopping_sale_snapshot_unit_option_id FK + String name + Int sequence } -"shopping_sellers" { +"shopping_sale_snapshot_unit_stocks" { String id PK - String shopping_member_id FK + String shopping_sale_snapshot_unit_id FK + String name + Float nominal_price + Float real_price + Int quantity + Int sequence +} +"shopping_sale_snapshot_unit_stock_choices" { + String id PK + String shopping_sale_snapshot_unit_stock_id FK + String shopping_sale_snapshot_unit_option_candidate_id FK + Int sequence +} +"shopping_channels" { + String id PK + String code UK + String name UK + Boolean exclusive DateTime created_at + DateTime updated_at DateTime deleted_at "nullable" } -"shopping_administrators" { +"shopping_channel_categories" { String id PK - String shopping_member_id UK + String shopping_channel_id FK + String parent_id FK "nullable" + String name DateTime created_at + DateTime updated_at DateTime deleted_at "nullable" } -"shopping_addresses" { +"shopping_sections" { String id PK - String mobile - String name - String country - String province - String city - String department - String possession - String zip_code - String special_note "nullable" + String code UK + String name UK DateTime created_at + DateTime updated_at + DateTime deleted_at "nullable" } -"shopping_customers" }o--|| "shopping_members" : member -"shopping_customers" }o--|| "shopping_external_users" : external_user -"shopping_customers" }o--|| "shopping_citizens" : citizen -"shopping_members" }o--|| "shopping_citizens" : citizen -"shopping_member_emails" }|--|| "shopping_members" : member -"shopping_sellers" |o--|| "shopping_members" : member -"shopping_administrators" |o--|| "shopping_members" : member +"shopping_sales" }|--|| "shopping_sections" : section +"shopping_sale_snapshots" }|--|| "shopping_sales" : sale +"shopping_sale_snapshot_channels" }|--|| "shopping_sale_snapshots" : snapshot +"shopping_sale_snapshot_channels" }|--|| "shopping_channels" : channel +"shopping_sale_snapshot_channel_categories" }|--|| "shopping_sale_snapshot_channels" : to_channel +"shopping_sale_snapshot_channel_categories" }|--|| "shopping_channel_categories" : category +"shopping_sale_snapshot_units" }|--|| "shopping_sale_snapshots" : snapshot +"shopping_sale_snapshot_unit_options" }|--|| "shopping_sale_snapshot_units" : unit +"shopping_sale_snapshot_unit_option_candidates" }|--|| "shopping_sale_snapshot_unit_options" : option +"shopping_sale_snapshot_unit_stocks" }|--|| "shopping_sale_snapshot_units" : unit +"shopping_sale_snapshot_unit_stock_choices" }|--|| "shopping_sale_snapshot_unit_stocks" : stock +"shopping_sale_snapshot_unit_stock_choices" }|--|| "shopping_sale_snapshot_unit_option_candidates" : candidate +"shopping_channel_categories" }|--|| "shopping_channels" : channel +"shopping_channel_categories" }o--o| "shopping_channel_categories" : parent ``` -### `shopping_customers` -Customer information, but not a person but a **connection** basis. +### `shopping_sales` +Seller **sales** products. -`shopping_customers` is an entity that literally embodies the information -of those who participated in the market as customers. By the way, the -`shopping_Customers` does not mean a person, but a **connection** basis. -Therefore, even if the same person connects to the shopping mall multiple, -multiple records are created in `shopping_customers`. +`shopping_sales` is an entity that embodies "product sales" (sales) +information registered by the [seller](#shopping_sellers). And the +main information of the sale is recorded in the sub +[shopping_sale_snapshots](#shopping_sale_snapshots), not in the main `shopping_sales`. +When a seller changes a previously registered item, the existing +`shopping_sales` record is not changed, but a new snapshot record is +created. -The first purpose of this is to track the customer's inflow path in detail, and -it is for cases where the same person enters as a non-member, puts items in the -[shopping cart](#shopping_cart_commodities) in advance, and only authenticates -their real name or registers/logs in at the moment of -[payment](#shopping_order_publishes). It is the second. Lastly, it is -to accurately track the activities that a person performs at the -shopping mall in various ways like below. - -- Same person comes from an [external service](#shopping_external_users) -- Same person creates multiple [accounts](#shopping_members) -- Same person makes a purchase as a non-member with only [real name authentication](#shopping_citizens) -- Same person acts both [seller](#shopping_sellers) and [admin](#shopping_administrators) at the same time - -Therefore, `shopping_customers` can have multiple records with the same -[shopping_citizens](#shopping_citizens), [shopping_members](#shopping_members), and -[shopping_external_users](#shopping_external_users). Additionally, if a customer signs up for -membership after verifying their real name or signs up for our service -after being a user of an external service, all related records are changed -at once. Therefore, identification and tracking of customers can be done -very systematically. +This is to preserve the [customer](#shopping_customers)'s +[purchase history](#shopping_orders) flawlessly after the customer +purchases a specific item, even if the seller changes the components or +price of the item. It is also intended to support sellers in so-called +A/B testing, which involves changing components or prices and measuring +the performance in each case. **Properties** - `id`: Primary Key. - - `shopping_channel_id`: Belonged channel's [shopping_channels.id](#shopping_channels) - - `shopping_member_id`: Belonged member's [shopping_members.id](#shopping_members) - - `shopping_external_user_id`: Belonged external service user's [shopping_external_users.id](#shopping_external_users) - - `shopping_citizen_id`: Belonged citizen's [shopping_citizens.id](#shopping_citizens) - - `href` - > Connection URL. - > - > [window.location.href](#window) - - `referrer` - > Referrer URL. - > - > [window.document.referrer](#window) - - `ip`: IP address, + - `shopping_section_id`: Belonged section's [shopping_sections.id](#shopping_sections) + - `shopping_seller_customer_id`: Belonged seller's [shopping_customers.id](#shopping_customers) - `created_at` > Creation time of record. > - > It means the time when the customer connected to the shopping mall. - -### `shopping_external_users` -External user information. - -`shopping_external_users` is an entity dsigned for when this system needs -to connect with external services and welcome their users as customers of -this service. - -For reference, customers who connect from an external service must have -this record, and the external service user is identified through the two -attributes `application` and `uid`. If a customer connected from an -external service completes [real-name authentication](#shopping_citizens) -from this service, each time the external service user reconnects to this -service and issues a new [customer](#shopping_customers) authentication -token, [real-name authentication](#shopping_citizens) begins with -completed. - -And `password` is the password issued to the user by the external service -system (the so-called permanent user authentication token), and is never -the actual user password. However, for customers who entered the same -`application` and `uid` as the current external system user, this is to -determine whether to view this as a correct external system user or a -violation. - -In addition, additional information received from external services can -be recorded in the `data` field in JSON format. - -**Properties** - - `id`: Primary Key. - - `application` - > Identifier code of the external service. + > Note that, this property is different with `opened_at`, which means + > the opening time of sale. + - `opened_at` + > Opening time of sale. > - > It can be same with [shopping_channels.code](#shopping_channels) in common. - - `uid`: Identifier key of external user from the external system. - - `nickname`: Nickname of external user in the external system. - - `data`: Additional information about external user from the external system. - - `password` - > Password of external user from the external system. + > If `null` value assigned, it means not opened yet. + - `closed_at` + > Closing time of sale. > - > This is a password issued to the user by an external service, and is - > by no means the actual user password. However, for customers who - > entered the same application and code as the current external system - > user, this is to determine whether to view this as a correct external - > system user or a violation. - - `created_at` - > Creation time of record. + > If `null` value assigned, the sale is forever. + - `paused_at` + > Paused time. > - > Another word, first time when the external user connected. - -### `shopping_citizens` -Citizen verification information. + > The time when seller paused the sale in some reason. + > + > [Customers](#shopping_customers) still can see the sale in list + > and detail page, but a label "The sale is paused for a while by seller" + > be attached. + - `suspended_at` + > Suspended time. + > + > The time when seller suspended the sale in some reason. + > + > [Customers](#shopping_customers) can't see the sale in list and + > detail page. It seems almost ssame with soft deletion, but there're + > some differences. + > + > At 1st, seller and [administrator](#shopping_administrators) can + > see suspended sale in list and detail page. At 2nd, seller can + > resume the sale at any time. -`shopping_citizens` is an entity that records the user's real name and -mobile input information. +### `shopping_sale_snapshots` +Sale snapshot information. -For reference, in South Korea, real name authentication is required for -e-commerce participants, so the `name` attribute is important. However, -the situation is different overseas, so in reality, `mobile` attributes -are the most important, and identification of individual users is also -done based on this mobile. +`shopping_sale_snapshots` is an entity representing snapshot record of +belonged [sale](#shopping_sales). The snapshot record is created +whenever the seller newly creates or updates the sale. -Of course, real name and mobile phone authentication information are -encrypted and stored. +Sale | Cart | Order +-----|------|------ +x | [shopping_carts](#shopping_carts) | [shopping_orders](#shopping_orders) +[shopping_sale_snapshots](#shopping_sale_snapshots) | [shopping_cart_commodities](#shopping_cart_commodities) | [shopping_order_goods](#shopping_order_goods) +[shopping_sale_snapshot_unit_stocks](#shopping_sale_snapshot_unit_stocks) | [shopping_cart_commodity_stocks](#shopping_cart_commodity_stocks) | x **Properties** - `id`: Primary Key. - - `shopping_channel_id` - > Belonged channel's [shopping_channels.id](#shopping_channels) - > - > This is to manage personal information separately for each channel, - > and also to recognize cases where the same citizen is authenticated - > through different channels. - - `mobile`: Mobile phone number. - - `name`: Real name, or equivalent name identifiable. + - `shopping_sale_id`: Belonged sale's [shopping_sales.id](#shopping_sales) - `created_at` > Creation time of record. > - > In other words, the 1st time of citizen activation. - - `deleted_at`: Deletion time of record. + > It means the time when the seller created or updated the sale. -### `shopping_members` -Member Account. +### `shopping_sale_snapshot_channels` +Target channel of sale snapshot to sell. -`shopping_members` is an entity that symbolizes the case when a user -signs up as a member of this system. +`shopping_sale_snapshot_channels` is an entity that expresses through +which [channel](#shopping_channels) a listing +[snapshot](#shopping_sale_snapshots) is sold, and is designed to +resolve the M:N relationship between two tables. -In addition, `shopping_members` itself is a supertype entity, forming -and managing subtypes for various types of members. However, -[shopping_customers](#shopping_customers) are an exception, and due to the nature of -their records being created on a per-connection basis, they are not -divided into separate subtype entities when they sign up for membership. +**Properties** + - `id`: Primary Key. + - `shopping_sale_snapshot_id`: Belonged snapshot's [shopping_sale_snapshots.id](#shopping_sale_snapshots). + - `shopping_channel_id`: Belonged channel's [shopping_channels.id](#shopping_channels). -For reference, `shopping_members` allows multiple subtypes. Therefore, -it is also possible for a [citizen](#shopping_citizens) to be sometimes -a [customer](#shopping_customers), sometimes a -[seller](#shopping_sellers), sometimes an -[administrator](#shopping_administrators), and so on. +### `shopping_sale_snapshot_channel_categories` +Category classification info of sale snapshot. -Of course, this is according to system theory, and it is unclear what -the planning will be like. +`shopping_sale_snapshot_channel_categories` is an entity that expresses +which [category](#shopping_channel_categories) the listing +[snapshot](#shopping_sale_snapshots) is classified into in each +[channel](#shopping_channels). + +It is designed to resolve the M:N relationship between +[shopping_sale_snapshots](#shopping_sale_snapshots) and [shopping_channel_categories](#shopping_channel_categories), +respectively. Of course, if the target category being referred to is a +major category, all minor categories belonging to it can also be used. **Properties** - `id`: Primary Key. - - `shopping_channel_id`: Belonged channel's [shopping_channels.id](#shopping_channels) - - `shopping_citizen_id`: Belonged citizen's [shopping_citizens.id](#shopping_citizens) - - `nickname`: Nickname. - - `password`: Password for log-in. - - `created_at` - > Creation time of record. - > - > In other words, the joining time. - - `updated_at`: Update time of record. - - `withdrawn_at`: Deletion time of record. + - `shopping_sale_snapshot_channel_id`: Belonged assigned channel of sale snapshot's [shopping_sale_snapshot_channels.id](#shopping_sale_snapshot_channels) + - `shopping_channel_category_id`: Belonged channel category's [shopping_channel_categories.id](#shopping_channel_categories) -### `shopping_member_emails` -Email address of member. +### `shopping_sale_snapshot_units` +Product composition information handled in the sale snapshot. -This system allows multiple email addresses to be registered for one -[member](#shopping_members). If you don't have to plan such multiple -email addresses, just use only one. +`shopping_sale_snapshot_units` is an entity that embodies the +"individual product" information handled in the +[sale snapshot](#shopping_sale_snapshots). -**Properties** - - `id`: - - `shopping_channel_id` - > Belonged channel's [shopping_channels.id](#shopping_channels) - > - > Duplicated attribute with [shopping_members.channel_id](#shopping_members), but - > just denormalized for composing unique constraint. - - `shopping_member_id`: Belonged member's [shopping_members.id](#shopping_members) - - `value`: Email address. - - `created_at`: Creation time of record. +For reference, the reason why `shopping_sale_snapshot_units` is separated +from [shopping_sale_snapshots](#shopping_sale_snapshots) by an algebraic relationship of +1: N is because there are some cases where multiple products are sold +in one listing. This is the case with so-called "bundled products". -### `shopping_sellers` -Seller information. +- Bundle from regular product (Macbook Set) +- main body +- keyboard +- mouse +- Apple Care (Free A/S Voucher) -`shopping_sellers` is an entity that embodies a person who registers -[sales](#shoppingsales) to operate selling activities, with -[membership](#shoppingmembers) joining. +And again, `shopping_sale_snapshot_units` does not in itself refer to +the final [stock](#shopping_sale_snapshot_unit_stocks) that the +customer will purchase. +The [final stock](#shopping_sale_snapshot_unit_stocks) can be +found only after selecting all given +[options](#shopping_sale_snapshot_unit_options) and their +[candidate](#shopping_sale_snapshot_unit_option_candidates) values. -For reference, unlike [customers](#shopping_customers) which can -participate even without [membership](#shopping_members) joining, -seller must join membership to operate sales. Also, seller must -do the [real-name authentication](#shopping_citizens), too. +For example, even if you buy a Macbook, the +[final stocks](#shopping_sale_snapshot_unit_stocks) are determined +only after selecting all the +[options](#shopping_sale_snapshot_unit_options) (CPU / RAM / SSD), etc. **Properties** - `id`: Primary Key. - - `shopping_member_id`: Belonged member's [shopping_members.id](#shopping_members). - - `created_at` - > Creation time of record. + - `shopping_sale_snapshot_id`: Belonged snapshot's [shopping_sale_snapshots.id](#shopping_sale_snapshots) + - `name`: Representative name of the unit. + - `primary` + > Whether the unit is primary or not. > - > Joining time as seller, and it can be different with - > [membership](#shopping_members) joining time. - - `deleted_at` - > Withdrawal time. + > Just a labeling value. + - `required` + > Whether the unit is required or not. > - > It can be different with [shopping_members.deleted_at](#shopping_members). - -### `shopping_administrators` -Administrator account. + > When the unit is required, the customer must select the unit. If do + > not select, customer can't buy it. + > + > For example, if there's a sale "Macbook Set" and one of the unit is + > the "Main Body", is it possible to buy the "Macbook Set" without the + > "Main Body" unit? This property is for that case. + - `sequence`: Sequence order in belonged snapshot. -`shopping_administrators` is an entity that embodies a person who manages -the shopping mall system, with [membership](#shopping_members) joining. +### `shopping_sale_snapshot_unit_options` +Individual option information on units for sale. -For reference, unlike [customers](#shopping_customers) which can -participate even without [membership](#shopping_members) joining, -administrator must join membership to operate sales. Also, administrator must -do the [real-name authentication](#shopping_citizens), too. +`shopping_sale_snapshot_unit_options` is a subsidiary entity of +[shopping_sale_snapshot_units](#shopping_sale_snapshot_units) that represents individual products +in the sale, and is an entity designed to represent individual option +information for the unit. + +- Examples of Options +- optional options +- Computer: CPU, RAM, SSD, etc. +- Clothes: size, color, style, etc. +- descriptive options +- Engrave +- Simple question + +If the `type` of option is a `variable` value in `"select"`, the +[final stock](#shopping_sale_snapshot_unit_stocks) that the customer +will purchase changes depending on the selection of the +[candidate](#shopping_sale_snapshot_unit_option_candidates) value. + +Conversely, if it is a `type` other than `"select"`, or if the `type` +is `"select"` but `variable` is `false`, this is an option that has no +meaning beyond simple information transfer. Therefore, no matter what +value the customer enters and chooses when purchasing it, the option in +this case does not affect the +[final stock](#shopping_sale_snapshot_unit_stocks). **Properties** - `id`: Primary Key. - - `shopping_member_id`: Belonged member's [shopping_members.id](#shopping_members). - - `created_at` - > Creation time of record. + - `shopping_sale_snapshot_unit_id`: Belonged unit's [shopping_sale_snapshot_units.id](#shopping_sale_snapshot_units) + - `name`: Name of the option. + - `type` + > Type of the option. > - > Joining time as an administrator, and can be different with - > [membership](#shopping_members) joining time. - - `deleted_at` - > Deletion time of record. + > - `select`: The way selecting one of the candidate values. + > - `boolean` + > - `number` + > - `string` + - `variable` + > Whether the option is variable or not. > - > Withdrawal time from administrator, and can be different with - > [shopping_members.deleted_at](#shopping_members). + > When `type` of current option is `"select"`, this attribute means + > whether selecting different + > [candidate](#shopping_sale_snapshot_unit_option_candidate) value + > affects the [final stock](#shopping_sale_snapshot_unit_stocks) + > or not. + > + > For reference, if `type` value is not `"select"`, this attribute + > is always `false`. + - `sequence`: Sequence order in belonged unit. -### `shopping_addresses` -The address information. +### `shopping_sale_snapshot_unit_option_candidates` +Selectable candidate values within an option. + +`shopping_sale_snapshot_unit_option_candidates` is an entity that +represents individual candidate values that can be selected from +[options](#shopping_sale_snapshot_unit_options) of the "select" type. + +- Example +- RAM: 8GB, 16GB, 32GB +- GPU: RTX 3060, RTX 4080, TESLA +- License: Private, Commercial, Educatiion + +By the way, if belonged [option](#shopping_sale_snapshot_unit_options) +is not "select" type, this entity never being used. **Properties** - `id`: Primary Key. - - `mobile`: Mobile number. - - `name` - > Representative name of the address. + - `shopping_sale_snapshot_unit_option_id`: Belonged option's [shopping_sale_snapshot_unit_options.id](#shopping_sale_snapshot_unit_options) + - `name`: Representative name of candidate value. + - `sequence`: Sequence order in option. + +### `shopping_sale_snapshot_unit_stocks` +Final component information on units for sale. + +`shopping_sale_snapshot_unit_stocks` is a subsidiary entity of +[shopping_sale_snapshot_units](#shopping_sale_snapshot_units) that represents a product catalog +for sale, and is a kind of final stock that is constructed by selecting +all [options](#shopping_sale_snapshot_unit_options) +(variable "select" type) and their +[candidate](#shopping_sale_snapshot_unit_option_candidates) values in +the belonging unit. It is the "good" itself that customers actually +purchase. + +- Product Name) MacBook +- Options +- CPU: { i3, i5, i7, i9 } +- RAM: { 8GB, 16GB, 32GB, 64GB, 96GB } +- SSD: { 256GB, 512GB, 1TB } +- Number of final stocks: 4 * 5 * 3 = 60 + +For reference, the total number of `shopping_sale_snapshot_unit_stocks` +records in an attribution unit can be obtained using Cartesian Product. +In other words, the value obtained by multiplying all the candidate +values that each (variable "select" type) option can have by the number +of cases is the total number of final stocks in the unit. + +Of course, without a single variable "select" type option, the final +stocks count in the unit is only 1. + +**Properties** + - `id`: Primary Key. + - `shopping_sale_snapshot_unit_id`: Belonged unit's [shopping_sale_snapshot_units.id](#shopping_sale_snapshot_units) + - `name`: Name of the final stock. + - `nominal_price` + > Nominal price. > - > Sometimes be receiver's name, and sometimes be place name. - - `country`: Target country. - - `province`: Target province. - - `city`: Target city. - - `department`: Department name. - - `possession`: Detailed address containing room number. - - `zip_code`: Zip code, or postal code. - - `special_note`: Special description if required. - - `created_at`: Creation time of record. + > This is not real price to pay, but just a nominal price to show. + > If this value is greater than the `real_price`, it would be shown + > like seller is giving a discount. + - `real_price`: Real price to pay. + - `quantity` + > Initial inventory quantity. + > + > If this stock has been sold over this quantity count, the stock can't + > be sold anymore, because of out of stock. In that case, the seller can + > supplement the inventory quantity by registering some + > [shopping_sale_snapshot_unit_stock_supplements](#shopping_sale_snapshot_unit_stock_supplements) records. + - `sequence`: Sequence order in belonged unit. +### `shopping_sale_snapshot_unit_stock_choices` +Selection information of final stock. -## Coins +`shopping_sale_snapshot_unit_stock_choices` is an entity that represents +which [option](#shopping_sale_snapshot_unit_options) of each `variable` +"select" `type` was selected for each stock and which +[candidate value](#shopping_sale_snapshot_unit_option_candidates) was +selected within it. + +Of course, if the bound [unit](#shopping_sale_snapshot_units) does not +have any [options](#shopping_sale_snapshot_unit_options), this entity +can also be ignored. + +**Properties** + - `id`: Primary Key. + - `shopping_sale_snapshot_unit_stock_id`: Belonged stock's [shopping_sale_snapshot_unit_stocks.id](#shopping_sale_snapshot_unit_stocks) + - `shopping_sale_snapshot_unit_option_candidate_id`: Belonged candidate's [shopping_sale_snapshot_unit_option_candidates.id](#shopping_sale_snapshot_unit_option_candidates) + - `sequence`: Sequence order in belonged stock. + +### `shopping_sale_snapshot_contents` +Content information of sale snapshot. + +`shopping_sale_snapshot_contents` is an entity embodies the body contents +of [sale snapshot](#shopping_sale_snapshots). Also, it contains +revert policy of the sale. + +**Properties** + - `id`: Primary Key. + - `shopping_sale_snapshot_id`: Belonged snapshot's [shopping_sale_snapshots.id](#shopping_sale_snapshots) + - `title`: Title of the content. + - `format` + > Format of the body content. + > + > Same meaning with extension like `html`, `md`, `txt`. + - `body`: The main body content. + - `revert_policy` + > Revert policy. + > + > This is essential in South Korea, but I don't know well in overseas. + > + > Just use when you need. + +### `shopping_sale_snapshot_content_files` +Attachment file of sale snapshot content. + +**Properties** + - `id`: + - `shopping_sale_snapshot_content_id`: + - `attachment_file_id`: + - `sequence`: + +### `shopping_sale_snapshot_tags` +Search tag of sale snapshot. + +**Properties** + - `id`: + - `shopping_sale_snapshot_id`: + - `value`: + - `sequence`: + +### `shopping_sale_snapshot_unit_stock_supplements` +Supplementation of inventory quantity of stock. + +You know what? If a stock has been sold over its +[initial inventory quantity](#shopping_sale_snapshot_unit_stocks), +the stock can't be sold anymore, because of out of stock. In that case, how the +[shopping_sellers](#shopping_sellers) should do? + +When the sotck is sold out, seller can supplement the inventory record by +registering this `shopping_sale_snapshot_unit_stock_supplements` record. Right, +this `shopping_sale_snapshot_unit_stock_supplements` is an entity that embodies +the supplementation of the inventory quantity of the belonged +[stock](#shopping_sale_snapshot_unit_stocks). + +**Properties** + - `id`: Primary Key. + - `shopping_sale_snapshot_unit_stock_id`: Belonged stock's [shopping_sale_snapshot_unit_stocks.id](#shopping_sale_snapshot_unit_stocks) + - `quantity`: Supplemented inventory quantity. + - `created_at` + > Creation time of record. + > + > When the inventory be supplemented. + + +## Carts ```mermaid erDiagram -"shopping_customers" { +"shopping_carts" { String id PK - String shopping_channel_id FK - String shopping_member_id FK "nullable" - String shopping_external_user_id FK "nullable" - String shopping_citizen_id FK "nullable" - String href - String referrer - String ip + String shopping_customer_id FK + String actor_type DateTime created_at + DateTime deleted_at "nullable" } -"shopping_citizens" { +"shopping_cart_commodities" { String id PK - String shopping_channel_id FK "nullable" - String mobile - String name + String shopping_cart_id FK + String shopping_sale_snapshot_id FK + Int volume DateTime created_at - DateTime deleted_at "nullable" + Boolean published } -"shopping_deposits" { +"shopping_cart_commodity_stocks" { String id PK - String code UK - String source - Int direction - DateTime created_at - DateTime deleted_at "nullable" + String shopping_cart_commodity_id FK + String shopping_sale_snapshot_unit_id FK + String shopping_sale_snapshot_unit_stock_id FK + Int quantity + Int sequence } -"shopping_deposit_histories" { +"shopping_cart_commodity_stock_choices" { String id PK - String shopping_deposit_id FK - String shopping_citizen_id FK - String source_id - Float value - DateTime created_at - DateTime cancelled_at "nullable" + String shopping_cart_commodity_stock_id FK + String shopping_sale_snapshot_unit_option_id FK + String shopping_sale_snapshot_unit_option_candidate_id FK "nullable" + String value "nullable" + Int sequence } -"shopping_deposit_charges" { +"shopping_sale_snapshots" { String id PK - String shopping_customer_id FK - Float amount + String shopping_sale_id FK DateTime created_at - DateTime deleted_at "nullable" } -"shopping_deposit_charge_publishes" { +"shopping_sale_snapshot_units" { String id PK - String shopping_deposit_charge_id FK - String password "nullable" - DateTime created_at - DateTime paid_at "nullable" - DateTime cancelled_at "nullable" + String shopping_sale_snapshot_id FK + String name + Boolean primary + Boolean required + Int sequence } -"shopping_mileages" { +"shopping_sale_snapshot_unit_options" { String id PK - String code UK - String source - Int direction - Float default "nullable" - DateTime created_at - DateTime deleted_at "nullable" + String shopping_sale_snapshot_unit_id FK + String name + String type + Boolean variable + Int sequence } -"shopping_mileage_histories" { +"shopping_sale_snapshot_unit_option_candidates" { String id PK - String shopping_mileage_id FK - String shopping_citizen_id FK - String source_id - Float value - DateTime created_at - DateTime cancelled_at "nullable" + String shopping_sale_snapshot_unit_option_id FK + String name + Int sequence } -"shopping_customers" }o--|| "shopping_citizens" : citizen -"shopping_deposit_histories" }|--|| "shopping_deposits" : deposit -"shopping_deposit_histories" }|--|| "shopping_citizens" : citizen -"shopping_deposit_charges" }|--|| "shopping_customers" : customer -"shopping_deposit_charge_publishes" |o--|| "shopping_deposit_charges" : charge -"shopping_mileage_histories" }|--|| "shopping_mileages" : mileage -"shopping_mileage_histories" }|--|| "shopping_citizens" : citizen +"shopping_sale_snapshot_unit_stocks" { + String id PK + String shopping_sale_snapshot_unit_id FK + String name + Float nominal_price + Float real_price + Int quantity + Int sequence +} +"shopping_cart_commodities" }|--|| "shopping_carts" : cart +"shopping_cart_commodities" }|--|| "shopping_sale_snapshots" : snapshot +"shopping_cart_commodity_stocks" }|--|| "shopping_cart_commodities" : commodity +"shopping_cart_commodity_stocks" }|--|| "shopping_sale_snapshot_units" : unit +"shopping_cart_commodity_stocks" }|--|| "shopping_sale_snapshot_unit_stocks" : stock +"shopping_cart_commodity_stock_choices" }|--|| "shopping_cart_commodity_stocks" : stock +"shopping_cart_commodity_stock_choices" }|--|| "shopping_sale_snapshot_unit_options" : option +"shopping_cart_commodity_stock_choices" }o--|| "shopping_sale_snapshot_unit_option_candidates" : candidate +"shopping_sale_snapshot_units" }|--|| "shopping_sale_snapshots" : snapshot +"shopping_sale_snapshot_unit_options" }|--|| "shopping_sale_snapshot_units" : unit +"shopping_sale_snapshot_unit_option_candidates" }|--|| "shopping_sale_snapshot_unit_options" : option +"shopping_sale_snapshot_unit_stocks" }|--|| "shopping_sale_snapshot_units" : unit ``` -### `shopping_deposits` -Meta information of the deposit. - -`shopping_deposits` is an entity that embodies the specifications for -incomes and outcomes at a shopping mall. In other words, -`shopping_deposits` is not [shopping_deposit_histories](#shopping_deposit_histories), which -refers to the deposit/outcome details of deposits, but is simply -metadata that specifies specifications for income/outcome scenarios. - -**Properties** - - `id`: Primary Key. - - `code`: Identifier code. - - `source`: The source table occuring the deposit event. - - `direction` - > Direction of deposit. - > - > - `1`: Income - > - `-1`: outcome - - `created_at`: Creation time of record. - - `deleted_at`: / Deletion time of record. +### `shopping_carts` +Shopping Cart. -### `shopping_deposit_histories` -Deposit income/outcome details of customers (citizens). +`shopping_carts` is literally a space where +[customer](#shopping_customers) temporarily stores products before +[purchase](#shopping_orders). -`shopping_deposit_histories` is an entity that embodies the -[customer](#shopping_customers)'s income/outcome history. +By the way, it is possible for [sellers](#shopping_sellers) or +[administrators](#shopping_administrators) to compose a shopping cart. +Of course, the reason why they can compose a shopping cart is not for +[purchasing](#shopping_orders), but for providing a shopping cart template +to [customers](#shopping_customers). -You can think of it as a kind of accounting ledger table. Therefore, -note that, `value` must have positive number only, even if it is a -outcome. The minus value must be expressed by multiplying the -[shopping_deposits.direction](#shopping_deposits) value of the corresponding. +Sale | Cart | Order +-----|------|------ +x | [shopping_carts](#shopping_carts) | [shopping_orders](#shopping_orders) +[shopping_sale_snapshots](#shopping_sale_snapshots) | [shopping_cart_commodities](#shopping_cart_commodities) | [shopping_order_goods](#shopping_order_goods) +[shopping_sale_snapshot_unit_stocks](#shopping_sale_snapshot_unit_stocks) | [shopping_cart_commodity_stocks](#shopping_cart_commodity_stocks) | x **Properties** - `id`: Primary Key. - - `shopping_deposit_id`: Belonged metadata's [shopping_deposits.id](#shopping_deposits) - - `shopping_citizen_id`: Belonged citizen's [shopping_citizens.id](#shopping_citizens) - - `source_id`: The source record occured deposit/outcome. - - `value` - > Income/outcome amount of deposit. + - `shopping_customer_id`: Belonged customer's [shopping_customers.id](#shopping_customers) + - `actor_type` + > Type of the cart creator. > - > It must be a positive number, and you can check - > [shopping_deposits.direction](#shopping_deposits) for incomes and outcomes. - > If you want to express the figures for incomes and outcomes as - > positive/negative numbers, you can also multiply this field value by - > the attributed [shopping_deposits.direction](#shopping_deposits) value. + > - customer + > - seller + > - administrator - `created_at`: Creation time of record. - - `cancelled_at`: Cancelled time of record. + - `deleted_at`: Deletion time of record. -### `shopping_deposit_charges` -Deposit deposit. +### `shopping_cart_commodities` +Item in a shopping cart. -`shopping_deposit_charges` is an entity that symbolizes the act of a -[customer](#shopping_customers) applying for a deposit to a shopping -mall. +`shopping_cart_commodities` is an entity that represents a +[snapshot](#shopping_sale_snapshots) of the items that +[customer](#shopping_customers) has placed into his +[shopping cart](#shopping_carts) with a +[purchase](#shopping_orders) in mind. And if the customer continues +this into an actual [order](#shopping_orders) in the future, +`shopping_cart_commodities` be changed to [shopping_order_goods](#shopping_order_goods). -However, `shopping_deposit_charges` expresses the customer's intention to -make a deposit, but it has not yet been confirmed. Only when the customer -completes the [payment](#shopping_deposit_charge_publishes) -will the deposit increase be confirmed. +And while adding a sale snapshot to the shopping cart, the customer +inevitably selects specific [units](#shopping_sale_snapshot_units) and +[final stocks](#shopping_sale_snapshot_unit_stocks) within the listing +snapshot. Information about these units and stocks is recorded in the +subsidiary entity [shopping_cart_commodity_stocks](#shopping_cart_commodity_stocks). Also, there is an +attribute `volume` that indicates how many sets of snapshots of the +target commodity will be purchased. This "volume" is a value that will be +multiplied by [shopping_cart_commodity_stocks.quantity](#shopping_cart_commodity_stocks), the quantity +for each component. **Properties** - `id`: Primary Key. - - `shopping_customer_id`: Belonged metadata's [shopping_deposits.id](#shopping_deposits) - - `amount`: Charging amount. + - `shopping_cart_id`: Belonged cart's [shopping_carts.id](#shopping_carts) + - `shopping_sale_snapshot_id`: Target snapshot's [shopping_sale_snapshots.id](#shopping_sale_snapshots) + - `volume` + > Volume count. + > + > The value multiplied to [shopping_cart_commodity_stocks.quantity](#shopping_cart_commodity_stocks). - `created_at`: Creation time of record. - - `deleted_at` - > Deletion time of record. + - `published` + > Whether be published or not. > - > Only when be stopped before publishing. - -### `shopping_deposit_charge_publishes` -Payment progress information for deposits. - -`shopping_deposit_charge_publishes` is an entity that embodies the process -of a [customer](#shopping_customers) applying for a deposit and making -a payment. - -Please note that the existence of the `shopping_deposit_charge_publishes` -record does not mean that payment has been completed. Payment is complete -only when payment (`paid_at`) is complete. This is what the -"process of payment" mentioned above means. + > Is current commodity ordered and be paid? + > + > Until purchase the commodity, the commodity can be reused to create new + > cart commodity. This variable can be computed by referencing + > [order](#Orders) related tables, but just denormalized for the + > performance reason. -However, even after payment has been made, there may be cases where it is -suddenly cancelled, so you must be careful about this as well. +### `shopping_cart_commodity_stocks` +Final stock information of commodity added to the shopping cart. -**Properties** - - `id`: Primary Key. - - `shopping_deposit_charge_id`: Belonged charge appliance's [shopping_deposit_charges.id](#shopping_deposit_charges) - - `password` - > Password for encryption. - > - > This shopping mall system uses a randomly issued password to encrypt - > payment history, and is completely unrelated to the user. - - `created_at` - > Creation time of record. - > - > Note that, this property does not mean the payment completion time. - - `paid_at` - > Completion time of payment. - > - > This property is the only way to know if the payment has been - > completed. If this property is null, the payment has not been - > completed yet. - - `cancelled_at`: The time when the payment was cancelled or reverted. +`shopping_cart_commodity_stocks` is a subsidiary entity of +[shopping_cart_commodities](#shopping_cart_commodities) that embodies the information of the +[snapshot](#shopping_sale_snapshots) of the items in the shopping cart, +and is a concept that corresponds to the individual +[units](#shopping_sale_snapshot_units) in the target item snapshot +and the [stock](#shopping_sale_snapshot_unit_stocks) finally selected +among those [units](#shopping_sale_snapshot_units). -### `shopping_mileages` -Meta information of mileage. +Therefore, if the customer selects multiple units and stocks from the +target sale snapshot, the attributed [shopping_cart_commodities](#shopping_cart_commodities) record +also has multiple corresponding `shopping_cart_commodity_stocks` records. -`shopping_mileages` is an entity that embodies specifications for mileage -deposits and outcomes at a shopping mall. In other words, -`shopping_mileages` is not [shopping_mileage_histories](#shopping_mileage_histories), which means -mileage deposit and outcome history, but is simply metadata that -specifies specifications for scenarios in which mileage is deposited and -withdrawn. +And `shopping_cart_commodity_stocks` has a `quantity` property that indicates +how many final stocks would be purchased in total. The final quantity +actually purchased can be multiplied by the +[shopping_cart_commodities.volume](#shopping_cart_commodities) value of the parent entity. **Properties** - `id`: Primary Key. - - `code`: Identifier code. - - `source`: The source table occuring the mileage event. - - `direction` - > Direction of mileage. - > - > - `1`: Income - > - `-1`: outcome - - `default` - > Default value of mileage. - > - > Possible to mit, and how to use this default value is up to the - > backend program. It is okay to use it as a default value when - > creating a new record, or percentage value to be applied. - - `created_at`: Creation time of record. - - `deleted_at`: Deletion time of record. + - `shopping_cart_commodity_id`: Belonged commodity's [shopping_cart_commodities.id](#shopping_cart_commodities) + - `shopping_sale_snapshot_unit_id`: Target unit's [shopping_sale_snapshot_units.id](#shopping_sale_snapshot_units) + - `shopping_sale_snapshot_unit_stock_id`: Target final stock's [shopping_sale_snapshot_unit_stocks.id](#shopping_sale_snapshot_unit_stocks) + - `quantity`: Quantity count. + - `sequence`: Sequence order in belonged cart commodity. -### `shopping_mileage_histories` -Mileagea income/outcome details of customers (citizens). +### `shopping_cart_commodity_stock_choices` +Option choice information for the final stock added to the shopping cart. -`shopping_mileage_histories` is an entity that embodies the -[customer](#shopping_customers)'s deposit/outcome history. +`shopping_cart_commodity_stock_choices` is a subsidiary entity of +[shopping_cart_commodity_stocks](#shopping_cart_commodity_stocks). It records which +[options](#shopping_sale_snapshot_unit_options) the customer +specifically used while putting a specific +[unit](#shopping_sale_snapshot_units) and specific +[stock](#shopping_sale_snapshot_unit_stocks) of the +[sale snapshot](#shopping_sale_snapshots) in the shopping cart, and +which [candidate values](#shopping_sale_snapshot_unit_option_candidates) +were selected or written within the shopping cart. -You can think of it as a kind of accounting ledger table. Therefore, -note that, `value` must have positive number only, even if it is a -outcome. The minus value must be expressed by multiplying the -[shopping_mileages.direction](#shopping_mileages) value of the corresponding. +Therefore, `shopping_cart_commodity_stock_choices` has reference properties +and predicate properties for candidate values in addition to references +to options. If the `type` of target option is "select", enter the +candidate value selected by the customer. Otherwise, enter the value +written by the customer. **Properties** - `id`: Primary Key. - - `shopping_mileage_id`: Belonged metadata's [shopping_mileages.id](#shopping_mileages) - - `shopping_citizen_id`: Belonged citizen's [shopping_citizens.id](#shopping_citizens) - - `source_id`: The source record occured income/outcome. - - `value` - > Income/outcome amount of mileage. - > - > It must be a positive number, and you can check - > [shopping_mileages.direction](#shopping_mileages) for incomes and outcomes. - > If you want to express the figures for incomes and outcomes as - > positive/negative numbers, you can also multiply this field value by - > the attributed [shopping_mileages.direction](#shopping_mileages) value. - - `created_at`: Creation time of record. - - `cancelled_at`: Cancelled time of record. + - `shopping_cart_commodity_stock_id`: Belonged cart-commodity-stock's [shopping_cart_commodity_stocks.id](#shopping_cart_commodity_stocks) + - `shopping_sale_snapshot_unit_option_id`: Target option's [shopping_sale_snapshot_unit_options.id](#shopping_sale_snapshot_unit_options) + - `shopping_sale_snapshot_unit_option_candidate_id`: Selected candidate's [shopping_sale_snapshot_unit_option_candidates.id](#shopping_sale_snapshot_unit_option_candidates) + - `value`: User-written value for descriptive option. + - `sequence`: Sequence order in belonged cart-commodity-stock. ## Orders ```mermaid erDiagram -"shopping_addresses" { - String id PK - String mobile - String name - String country - String province - String city - String department - String possession - String zip_code - String special_note "nullable" - DateTime created_at -} -"shopping_cart_commodities" { - String id PK - String shopping_cart_id FK - String shopping_sale_snapshot_id FK - Int volume - DateTime created_at - Boolean published -} -"shopping_cart_commodity_stocks" { - String id PK - String shopping_cart_commodity_id FK - String shopping_sale_snapshot_unit_id FK - String shopping_sale_snapshot_unit_stock_id FK - Int quantity - Int sequence -} "shopping_orders" { String id PK String shopping_customer_id FK @@ -1728,7 +1451,35 @@ erDiagram DateTime started_at DateTime completed_at "nullable" } -"shopping_cart_commodity_stocks" }|--|| "shopping_cart_commodities" : commodity +"shopping_addresses" { + String id PK + String mobile + String name + String country + String province + String city + String department + String possession + String zip_code + String special_note "nullable" + DateTime created_at +} +"shopping_cart_commodities" { + String id PK + String shopping_cart_id FK + String shopping_sale_snapshot_id FK + Int volume + DateTime created_at + Boolean published +} +"shopping_cart_commodity_stocks" { + String id PK + String shopping_cart_commodity_id FK + String shopping_sale_snapshot_unit_id FK + String shopping_sale_snapshot_unit_stock_id FK + Int quantity + Int sequence +} "shopping_orders" }o--|| "shopping_addresses" : address "shopping_order_goods" }|--|| "shopping_orders" : order "shopping_order_goods" }|--|| "shopping_cart_commodities" : commodity @@ -1737,6 +1488,7 @@ erDiagram "shopping_delivery_pieces" }|--|| "shopping_order_goods" : good "shopping_delivery_pieces" }|--|| "shopping_cart_commodity_stocks" : cart_commodity_stock "shopping_delivery_journeys" }|--|| "shopping_deliveries" : delivery +"shopping_cart_commodity_stocks" }|--|| "shopping_cart_commodities" : commodity ``` ### `shopping_orders` @@ -1778,802 +1530,1050 @@ x | [shopping_carts](#shopping_carts) | [shopping_orders](#shopping_orders) > [shopping_order_publishes.cancelled_at](#shopping_order_publishes) instead, or utilize > [shopping_order_good_reverts](#shopping_order_good_reverts) instead. -### `shopping_order_goods` -Information about the individual goods that make up your order. +### `shopping_order_goods` +Information about the individual goods that make up your order. + +`shopping_order_goods` is an entity that represents each good ordered +by a [customer](#shopping_customers), and the record is created in the +process of upgrading the product [commodity](#shopping_cart_commodities) +in the [shopping cart](#shopping_carts) to a good due to the customer's +[order request](#shopping_orders). + +And `shopping_order_goods`, like [shopping_cart_commodities](#shopping_cart_commodities), is a concept +that corresponds to the listing +[sale snapshot](#shopping_sale_snapshots). + +For reference, `shopping_order_goods` also contains `volume` information +separately from the belonging [shopping_cart_commodities.volume](#shopping_cart_commodities). This is +because there are some cases where you put 3 books in your shopping cart +and then change them to 4 during the actual order application process. +This is to increase the reusability of the shopping cart by changing the +volume attribute of the current entity rather than directly changing the +shopping_cart_commodities information. + +In addition, `shopping_order_goods` becomes the most basic unit for +the post-order process, that is, after service (A/S). For example, +after receiving a customer's product, confirming the order is recorded +in the `confirmed_at` attribute. Additionally, `shopping_order_goods` is +the unit in which customers issue issues or request exchanges or refunds +for ordered products. + +**Properties** + - `id`: Primary Key. + - `shopping_order_id`: Belonged order's [shopping_orders.id](#shopping_orders) + - `shopping_cart_commodity_id`: Belonged cart commodity's [shopping_cart_commodities.id](#shopping_cart_commodities) + - `shopping_seller_id` + > Belonged seller's [shopping_sellers.id](#shopping_sellers) + > + > It can be computed by referencing related [shopping_sales](#shopping_sales), + > but denormalized for performance reason. + - `volume` + > Volume count. + > + > The value multiplied to [shopping_cart_commodity_stocks.quantity](#shopping_cart_commodity_stocks). + > It's purpose is exactly same with [shopping_cart_commodities.volume](#shopping_cart_commodities), + > but rewritten because the [shopping_cart_commodities](#shopping_cart_commodities) records are + > reusable until payment. + - `sequence`: Sequence order(?) in belonged order. + - `confirmed_at` + > Confirmation time of order good. + > + > When be confirmed, customer can't request refund or exchange. + > + > The confirmation be accomplished by following cases. + > + > - Customer does it directly. + > - 14 days after the delivery. + +### `shopping_order_publishes` +Order completion and payment information. + +`shopping_order_publishes` is an entity that embodies the series of +processes in which a customer pays for his or her +[order](#shopping_orders), thereby completing the order. And only after +the order is completed, can the seller recognize that the customer has +purchased his product. + +By the way, please note that just because the `shopping_order_publishes` +record exists, it does not mean that the payment has been completed. +Of course, with "credit cards" and "Google Pay", payment application and +payment occur at the same time. However, there are some cases where +payment is made after the payment application, such as "bank transfer" or +"virtual account payment". Therefore, to see the completion of payment, +be sure to check the `paid_at` property. + +In addition, even after payment has been made, there may be cases where +it is suddenly cancelled, so please be aware of this as well. + +**Properties** + - `id`: Primary Key. + - `shopping_order_id`: Belonged order's [shopping_orders.id](#shopping_orders) + - `password` + > Password for encryption. + > + > This shopping mall system uses a randomly issued password to + > encrypt payment history, and is completely unrelated to the user. + - `created_at` + > Creation time of record. + > + > Note that, this property does not mean the payment completion time. + - `paid_at` + > Completion time of payment. + > + > This property is the only way to know if the payment has been + > completed. If this property is `null`, the payment has not been + > completed yet. + - `cancelled_at`: The time when the payment was cancelled or reverted. + +### `shopping_deliveries` +Delivery information. + +When delivering [goods](#shopping_order_goods) to +[customer](#shopping_customers), [seller](#shopping_selleres) can deliver +multiple [stocks](#shopping_sale_snapshot_unit_stocks), goods at once. Also, +it is possible to deliver a stock or good in multiple times due to physical +restriction like volume or weight problem. + +As you can see from above, the relationship between delivery with +[order](#shopping_orders) (or good) is not 1: 1 or N: 1, but M: N. Entity +`shopping_deliveries` has been designed to represent such relationship, by +referencing target stocks or goods through subsidiary entity +[shopping_delivery_pieces](#shopping_delivery_pieces). + +Also, delivery does not end with only one step. It has multiple processes like +manufacturing, planning, shipping and delivering. Those steps are represented by +another subsidiary entity [shopping_delivery_journeys](#shopping_delivery_journeys). + +**Properties** + - `id`: Primary Key. + - `shopping_seller_customer_id`: Belonged seller's [id](#shopping_sellers) + - `invoice_code`: Invoice code if exists. + - `created_at`: Creation time of record. + +### `shopping_delivery_pieces` +Which stocks are delivered. + +`shopping_delivery_pieces` is a subsidiary entity of [shopping_deliveries](#shopping_deliveries), +describing how much quantity is delivered for each +[stock](#shopping_sale_snapshot_unit_stocks) in [shopping_orders](#shopping_orders). + +For reference, as an order can be delivered in multiple times due to volume or +weight problem, it is possible to have multiple `shopping_delivery_pieces` records +for a single stock. + +**Properties** + - `id`: Primary Key. + - `shopping_delivery_id`: Belonged delivery's [id](#shopping_deliveries) + - `shopping_order_good_id`: Target good's [id](#shopping_order_goods) + - `shopping_cart_commodity_stock_id`: Target stock-wrapper's [id](#shopping_sale_snapshot_unit_stocks) + - `quantity` + > Quantity count. + > + > It can be precision value to express splitted shipping. + - `sequence`: Sequence order in belonged delivery. + +### `shopping_delivery_journeys` +Journey of delivery. + +`shopping_delivery_journeys` is a subsidiary entity of [shopping_deliveries](#shopping_deliveries), +describing each journey of the delivery. For reference, the word journey means +each step of the delivery process, such as preparing, shipping, and delivering +[goods](#shopping_order_goods) to the [customer](#shopping_customers). + +**Properties** + - `id`: Primary Key. + - `shopping_delivery_id`: + - `type` + > Type of journey. + > + > - preparing + > - shipping + > - delivering + - `title`: Title of journey. + - `description`: Description of journey. + - `created_at`: Creation time of record. + - `started_at`: Start time of journey. + - `completed_at`: Completion time of journey. + + +## Coupons +```mermaid +erDiagram +"shopping_coupons" { + String id PK + String shopping_customer_id FK + String actor_type + String name + String access + Boolean exclusive + String unit + Float value + Float threshold "nullable" + Int limit "nullable" + Int volume "nullable" + Int volume_per_citizen "nullable" + Int expired_in "nullable" + DateTime expired_at "nullable" + DateTime opened_at "nullable" + DateTime closed_at "nullable" + DateTime created_at + DateTime updated_at + DateTime deleted_at "nullable" +} +"shopping_coupon_criterias" { + String id PK + String shopping_coupon_id FK + String type + String direction + Int sequence +} +"shopping_coupon_section_criterias" { + String id PK + String shopping_section_id FK +} +"shopping_coupon_channel_criterias" { + String id PK + String shopping_channel_id FK + String shopping_channel_category_id FK "nullable" +} +"shopping_coupon_seller_criterias" { + String id PK + String shopping_seller_id FK +} +"shopping_coupon_sale_criterias" { + String id PK + String shopping_sale_id FK +} +"shopping_coupon_funnel_criterias" { + String id PK + String kind + String key "nullable" + String value +} +"shopping_coupon_tickets" { + String id PK + String shopping_customer_id FK + String shopping_coupon_id FK + String shopping_coupon_disposable_id FK "nullable" + DateTime created_at + DateTime expired_at "nullable" +} +"shopping_coupon_ticket_payments" { + String id PK + String shopping_coupon_ticket_id FK + String shopping_order_id FK + Int sequence + DateTime created_at + DateTime deleted_at "nullable" +} +"shopping_coupon_disposables" { + String id PK + String shopping_coupon_id FK + String code UK + DateTime created_at + DateTime expired_at "nullable" +} +"shopping_coupon_criterias" }|--|| "shopping_coupons" : coupon +"shopping_coupon_section_criterias" |o--|| "shopping_coupon_criterias" : base +"shopping_coupon_channel_criterias" |o--|| "shopping_coupon_criterias" : base +"shopping_coupon_seller_criterias" |o--|| "shopping_coupon_criterias" : base +"shopping_coupon_sale_criterias" |o--|| "shopping_coupon_criterias" : base +"shopping_coupon_funnel_criterias" |o--|| "shopping_coupon_criterias" : base +"shopping_coupon_tickets" }|--|| "shopping_coupons" : coupon +"shopping_coupon_tickets" |o--|| "shopping_coupon_disposables" : disposable +"shopping_coupon_ticket_payments" |o--|| "shopping_coupon_tickets" : ticket +"shopping_coupon_disposables" }|--|| "shopping_coupons" : coupon +``` + +### `shopping_coupons` +Discount coupon. -`shopping_order_goods` is an entity that represents each good ordered -by a [customer](#shopping_customers), and the record is created in the -process of upgrading the product [commodity](#shopping_cart_commodities) -in the [shopping cart](#shopping_carts) to a good due to the customer's -[order request](#shopping_orders). +`shopping_coupons` is an entity that symbolizes discount coupons at a +shopping mall. -And `shopping_order_goods`, like [shopping_cart_commodities](#shopping_cart_commodities), is a concept -that corresponds to the listing -[sale snapshot](#shopping_sale_snapshots). +Note that, `shopping_coupons` only contains specification information +about discount coupons. Please keep in mind that this is a different +concept from [shopping_coupon_tickets](#shopping_coupon_tickets), which refers to the issuance +of a discount coupon, or [shopping_coupon_ticket_payments](#shopping_coupon_ticket_payments), which +refers to its payment. -For reference, `shopping_order_goods` also contains `volume` information -separately from the belonging [shopping_cart_commodities.volume](#shopping_cart_commodities). This is -because there are some cases where you put 3 books in your shopping cart -and then change them to 4 during the actual order application process. -This is to increase the reusability of the shopping cart by changing the -volume attribute of the current entity rather than directly changing the -shopping_cart_commodities information. +Additionally, discount coupons are applied on an +[order-by-order](#shopping_orders) basis, but each has its own unique +restrictions. For example, a coupon with +[shopping_coupon_seller_criterias](#shopping_coupon_seller_criterias) may or may not be used only for +[snapshots](#shopping_sale_snapshots) of listings registered by the +[seller](#shopping_sellers). Also, there are restrictions such as +minimum amount restrictions for using discount coupons and maximum discount +amount limits. -In addition, `shopping_order_goods` becomes the most basic unit for -the post-order process, that is, after service (A/S). For example, -after receiving a customer's product, confirming the order is recorded -in the `confirmed_at` attribute. Additionally, `shopping_order_goods` is -the unit in which customers issue issues or request exchanges or refunds -for ordered products. +In addition, you can set whether to issue discount coupons publicly or give +them only to people who know the specific issuing code. In addition, there +are restrictions such as issued discount coupons having an expiration date +or being issued only to customers who came in through a specific channel. + +For more information, please refer to the properties below and the +subsidiary entities described later. **Properties** - `id`: Primary Key. - - `shopping_order_id`: Belonged order's [shopping_orders.id](#shopping_orders) - - `shopping_cart_commodity_id`: Belonged cart commodity's [shopping_cart_commodities.id](#shopping_cart_commodities) - - `shopping_seller_id` - > Belonged seller's [shopping_sellers.id](#shopping_sellers) + - `shopping_customer_id`: Belonged administrator or seller's [shopping_customers.id](#shopping_customers) + - `actor_type` + > Type of the coupon creator. > - > It can be computed by referencing related [shopping_sales](#shopping_sales), - > but denormalized for performance reason. + > - administrator + > - seller + - `name`: Reprensentative name of coupon. + - `access` + > Access level of coupon. + > + > - `public`: possible to find from public API + > - `private`: unable to find from public API + > - arbitrarily assigned by the seller or administrator + > - issued from one-time link + - `exclusive` + > Exclusivity or not. + > + > An exclusive discount coupon refers to a discount coupon that has an + > exclusive relationship with other discount coupons and can only be used + > alone. That is, when an exclusive discount coupon is used, no other + > discount coupon can be used for the same [order](#shopping_orders) + > or [good](#shopping_order_goods). + > + > Please note that this `exclusive` attribute is a very different + > concept from `multiplicative`, which means whether the same coupon + > can be multiplied and applied to multiple coupons of the same order, + > so please do not confuse them. + - `unit` + > Discount unit. + > + > - amount: Absolute value + > - percent: 0 ~ 100 % + - `value` + > Discount value. + > + > If `unit` is percent, range of value is limited from 0 to 100. + - `threshold` + > Minimum purchase amount for discount. + > + > When setting this value, discount coupons cannot be applied to + > order totals that are less than this value. + - `limit` + > Maximum amount available for discount. + > + > When this value is set, no further discount will be given no matter + > how much you order. - `volume` - > Volume count. + > Limited quantity issued. > - > The value multiplied to [shopping_cart_commodity_stocks.quantity](#shopping_cart_commodity_stocks). - > It's purpose is exactly same with [shopping_cart_commodities.volume](#shopping_cart_commodities), - > but rewritten because the [shopping_cart_commodities](#shopping_cart_commodities) records are - > reusable until payment. - - `sequence`: Sequence order(?) in belonged order. - - `confirmed_at` - > Confirmation time of order good. + > If there is a limit to the quantity issued, it becomes impossible to + > issue tickets exceeding this value. > - > When be confirmed, customer can't request refund or exchange. + > In other words, the concept of N coupons being issued on a first-come, + > first-served basis is created. + - `volume_per_citizen` + > Limited quantity issued per person. > - > The confirmation be accomplished by following cases. + > As a limit to the total amount of issuance per person, it is common to + > assign 1 to limit duplicate issuance to the same citizen, or to use + > the `NULL` value to set no limit. > - > - Customer does it directly. - > - 14 days after the delivery. - -### `shopping_order_publishes` -Order completion and payment information. + > Of course, by assigning a value of N, the total amount issued to the + > same citizen can be limited. + - `expired_in` + > Expiration day(s) value. + > + > The concept of expiring N days after a discount coupon ticket is issued. + > + > Therefore, customers must use the ticket within N days, if possible, + > from the time it is issued. + - `expired_at` + > Expiration date. + > + > A concept that expires after YYYY-MM-DD after a discount coupon ticket + > is issued. + > + > Double restrictions are possible with `expired_in`, of which the one + > with the shorter expiration date is used. + - `opened_at`: Issuance starting date. + - `closed_at` + > Issuance end date. + > + > Tickets cannot be issued after this time. + > + > However, previously issued tickets can still be used until their + > expiration date. + - `created_at`: Creation time of record. + - `updated_at` + > Update time of record. + > + > Only possible to update until `opened_at`. + - `deleted_at` + > Deletion time of record. + > + > Pre-issued tickets can still be used until their expiration date. -`shopping_order_publishes` is an entity that embodies the series of -processes in which a customer pays for his or her -[order](#shopping_orders), thereby completing the order. And only after -the order is completed, can the seller recognize that the customer has -purchased his product. +### `shopping_coupon_criterias` +Supertype for the applicable conditions of the discount coupon. -By the way, please note that just because the `shopping_order_publishes` -record exists, it does not mean that the payment has been completed. -Of course, with "credit cards" and "Google Pay", payment application and -payment occur at the same time. However, there are some cases where -payment is made after the payment application, such as "bank transfer" or -"virtual account payment". Therefore, to see the completion of payment, -be sure to check the `paid_at` property. +`shopping_coupon_criterias` is a supertype entity that embodies the +conditions for applying a [discount coupon](#shopping_coupons). All +subtype entities that wish to impose constraints on the reference unit of +a discount coupon were created by inheriting this. For example, the +[shopping_coupon_section_criterias](#shopping_coupon_section_criterias) entity, designed to limit +application to a specific [section](#shopping_sections), inherits this +entity `shopping_coupon_criterias`. -In addition, even after payment has been made, there may be cases where -it is suddenly cancelled, so please be aware of this as well. +In addition, constraints on reference units can be specified through the +`direction` property to proceed as an inclusion condition or, conversely, +as an exclusion condition. If the direction value is "include", the coupon +is applicable only to the reference object. Conversely, if the direction +value is "exclude", it is a coupon that cannot be applied to the reference +object. **Properties** - `id`: Primary Key. - - `shopping_order_id`: Belonged order's [shopping_orders.id](#shopping_orders) - - `password` - > Password for encryption. - > - > This shopping mall system uses a randomly issued password to - > encrypt payment history, and is completely unrelated to the user. - - `created_at` - > Creation time of record. + - `shopping_coupon_id`: Belonged coupon's [shopping_coupons.id](#shopping_coupons) + - `type` + > Type of criteria. > - > Note that, this property does not mean the payment completion time. - - `paid_at` - > Completion time of payment. + > It means which subtype being used. + - `direction` + > Direction of criteria. > - > This property is the only way to know if the payment has been - > completed. If this property is `null`, the payment has not been - > completed yet. - - `cancelled_at`: The time when the payment was cancelled or reverted. + > - include + > - exclude + - `sequence`: Sequence order in belonged coupon. -### `shopping_deliveries` -Delivery information. +### `shopping_coupon_section_criterias` +Conditions for sections of discount coupons. -When delivering [goods](#shopping_order_goods) to -[customer](#shopping_customers), [seller](#shopping_selleres) can deliver -multiple [stocks](#shopping_sale_snapshot_unit_stocks), goods at once. Also, -it is possible to deliver a stock or good in multiple times due to physical -restriction like volume or weight problem. +`shopping_coupon_section_criterias` is a subtype entity of +[shopping_coupon_criterias](#shopping_coupon_criterias) and is used when setting conditions for +a specific [section](#shopping_sections). -As you can see from above, the relationship between delivery with -[order](#shopping_orders) (or good) is not 1: 1 or N: 1, but M: N. Entity -`shopping_deliveries` has been designed to represent such relationship, by -referencing target stocks or goods through subsidiary entity -[shopping_delivery_pieces](#shopping_delivery_pieces). +If the [shopping_coupon_criterias.direction](#shopping_coupon_criterias) value is "include", +the coupon can only be used for that section. Conversely, if it is +"exclude", the coupon cannot be used. And if there are multiple +`shopping_coupon_section_criterias` records within one coupon, conditions +are applied on a bundle basis. Coupons may or may not be applicable to +eligible sections. -Also, delivery does not end with only one step. It has multiple processes like -manufacturing, planning, shipping and delivering. Those steps are represented by -another subsidiary entity [shopping_delivery_journeys](#shopping_delivery_journeys). +**Properties** + - `id`: Primary Key. + - `shopping_section_id`: Target section's [shopping_coupon_criterias.id](#shopping_coupon_criterias) + +### `shopping_coupon_channel_criterias` +Conditions for channels of discount coupons. + +`shopping_coupon_channel_criterias` is a subtype entity of +[shopping_coupon_criterias](#shopping_coupon_criterias) and is used when setting conditions on +a specific [channel](#shopping_channels) or +[category](#shopping_channel_categories) of that channel. + +If the [shopping_coupon_criterias.direction](#shopping_coupon_criterias) value is "include", +the coupon can only be used for that channel (or category). Conversely, +if it is "exclude", it is a coupon that cannot be used. And if there are +multiple `shopping_coupon_channel_criterias` records within one coupon, +conditions are applied on a bundle basis. Coupons may or may not be +applicable for target channels and categories. **Properties** - `id`: Primary Key. - - `shopping_seller_customer_id`: Belonged seller's [id](#shopping_sellers) - - `invoice_code`: Invoice code if exists. - - `created_at`: Creation time of record. + - `shopping_channel_id`: Target channel's [shopping_channels.id](#shopping_channels) + - `shopping_channel_category_id`: Target channel category's [shopping_channel_categories.id](#shopping_channel_categories) -### `shopping_delivery_pieces` -Which stocks are delivered. +### `shopping_coupon_seller_criterias` +Conditions for sellers of discount coupons. -`shopping_delivery_pieces` is a subsidiary entity of [shopping_deliveries](#shopping_deliveries), -describing how much quantity is delivered for each -[stock](#shopping_sale_snapshot_unit_stocks) in [shopping_orders](#shopping_orders). +`shopping_coupon_seller_criterias` is a subtype entity of +[shopping_coupon_criterias](#shopping_coupon_criterias) and is used when setting conditions for +a specific [seller](#shopping_sellers). -For reference, as an order can be delivered in multiple times due to volume or -weight problem, it is possible to have multiple `shopping_delivery_pieces` records -for a single stock. +If the [shopping_coupon_criterias.direction](#shopping_coupon_criterias) value is "include", the +coupon can only be used for that seller. Conversely, if it is "exclude", +the coupon cannot be used. -**Properties** - - `id`: Primary Key. - - `shopping_delivery_id`: Belonged delivery's [id](#shopping_deliveries) - - `shopping_order_good_id`: Target good's [id](#shopping_order_goods) - - `shopping_cart_commodity_stock_id`: Target stock-wrapper's [id](#shopping_sale_snapshot_unit_stocks) - - `quantity` - > Quantity count. - > - > It can be precision value to express splitted shipping. - - `sequence`: Sequence order in belonged delivery. +And if there are multiple `shopping_coupon_seller_criterias` records within +one coupon, conditions are applied on a bundle basis. Coupons may or may +not be applicable to eligible sellers. -### `shopping_delivery_journeys` -Journey of delivery. +**Properties** + - `id`: PK + FK. + - `shopping_seller_id`: Target seller's [shopping_sellers.id](#shopping_sellers) -`shopping_delivery_journeys` is a subsidiary entity of [shopping_deliveries](#shopping_deliveries), -describing each journey of the delivery. For reference, the word journey means -each step of the delivery process, such as preparing, shipping, and delivering -[goods](#shopping_order_goods) to the [customer](#shopping_customers). +### `shopping_coupon_sale_criterias` +Conditions for a specific item in a discount coupon. -**Properties** - - `id`: Primary Key. - - `shopping_delivery_id`: - - `type` - > Type of journey. - > - > - preparing - > - shipping - > - delivering - - `title`: Title of journey. - - `description`: Description of journey. - - `created_at`: Creation time of record. - - `started_at`: Start time of journey. - - `completed_at`: Completion time of journey. +`shopping_coupon_sale_criterias` is a subtype entity of +[shopping_coupon_criterias](#shopping_coupon_criterias) and is used when setting conditions for +a specific [sale](#shopping_sales). +If the [shopping_coupon_criterias.direction](#shopping_coupon_criterias) value is "include", +the coupon can only be used for that item. Conversely, if it is "exclude", +it is a coupon that cannot be used. -## Carts -```mermaid -erDiagram -"shopping_sale_snapshots" { - String id PK - String shopping_sale_id FK - DateTime created_at -} -"shopping_sale_snapshot_units" { - String id PK - String shopping_sale_snapshot_id FK - String name - Boolean primary - Boolean required - Int sequence -} -"shopping_sale_snapshot_unit_options" { - String id PK - String shopping_sale_snapshot_unit_id FK - String name - String type - Boolean variable - Int sequence -} -"shopping_sale_snapshot_unit_option_candidates" { - String id PK - String shopping_sale_snapshot_unit_option_id FK - String name - Int sequence -} -"shopping_sale_snapshot_unit_stocks" { - String id PK - String shopping_sale_snapshot_unit_id FK - String name - Float nominal_price - Float real_price - Int quantity - Int sequence -} -"shopping_carts" { - String id PK - String shopping_customer_id FK - String actor_type - DateTime created_at - DateTime deleted_at "nullable" -} -"shopping_cart_commodities" { - String id PK - String shopping_cart_id FK - String shopping_sale_snapshot_id FK - Int volume - DateTime created_at - Boolean published -} -"shopping_cart_commodity_stocks" { - String id PK - String shopping_cart_commodity_id FK - String shopping_sale_snapshot_unit_id FK - String shopping_sale_snapshot_unit_stock_id FK - Int quantity - Int sequence -} -"shopping_cart_commodity_stock_choices" { - String id PK - String shopping_cart_commodity_stock_id FK - String shopping_sale_snapshot_unit_option_id FK - String shopping_sale_snapshot_unit_option_candidate_id FK "nullable" - String value "nullable" - Int sequence -} -"shopping_sale_snapshot_units" }|--|| "shopping_sale_snapshots" : snapshot -"shopping_sale_snapshot_unit_options" }|--|| "shopping_sale_snapshot_units" : unit -"shopping_sale_snapshot_unit_option_candidates" }|--|| "shopping_sale_snapshot_unit_options" : option -"shopping_sale_snapshot_unit_stocks" }|--|| "shopping_sale_snapshot_units" : unit -"shopping_cart_commodities" }|--|| "shopping_carts" : cart -"shopping_cart_commodities" }|--|| "shopping_sale_snapshots" : snapshot -"shopping_cart_commodity_stocks" }|--|| "shopping_cart_commodities" : commodity -"shopping_cart_commodity_stocks" }|--|| "shopping_sale_snapshot_units" : unit -"shopping_cart_commodity_stocks" }|--|| "shopping_sale_snapshot_unit_stocks" : stock -"shopping_cart_commodity_stock_choices" }|--|| "shopping_cart_commodity_stocks" : stock -"shopping_cart_commodity_stock_choices" }|--|| "shopping_sale_snapshot_unit_options" : option -"shopping_cart_commodity_stock_choices" }o--|| "shopping_sale_snapshot_unit_option_candidates" : candidate -``` +And if there are multiple shopping_coupon_sale_criterias records within one coupon, conditions are applied on a bundle basis. Coupons that may or may not be applicable to target properties. -### `shopping_carts` -Shopping Cart. +**Properties** + - `id`: PK + FK. + - `shopping_sale_id`: Target sale's [shopping_sales.id](#shopping_sales) -`shopping_carts` is literally a space where -[customer](#shopping_customers) temporarily stores products before -[purchase](#shopping_orders). +### `shopping_coupon_funnel_criterias` +Limit the funnel of discount coupons. -By the way, it is possible for [sellers](#shopping_sellers) or -[administrators](#shopping_administrators) to compose a shopping cart. -Of course, the reason why they can compose a shopping cart is not for -[purchasing](#shopping_orders), but for providing a shopping cart template -to [customers](#shopping_customers). +`shopping_coupon_funnel_criterias` is a subtype entity of +[shopping_coupon_criterias](#shopping_coupon_criterias), and is used when you want to issue or +exclude discount coupons only to [customers](#shopping_customers) who +came from a specific path. -Sale | Cart | Order ------|------|------ -x | [shopping_carts](#shopping_carts) | [shopping_orders](#shopping_orders) -[shopping_sale_snapshots](#shopping_sale_snapshots) | [shopping_cart_commodities](#shopping_cart_commodities) | [shopping_order_goods](#shopping_order_goods) -[shopping_sale_snapshot_unit_stocks](#shopping_sale_snapshot_unit_stocks) | [shopping_cart_commodity_stocks](#shopping_cart_commodity_stocks) | x +And funnel restrictions are possible in 3 ways: The first is +[shopping_customers.referrer](#shopping_customers), and by parsing +[shopping_customers.href](#shopping_customers), which records the customer's access +address, restrictions can be made in units of specific URLs or variables. **Properties** - - `id`: Primary Key. - - `shopping_customer_id`: Belonged customer's [shopping_customers.id](#shopping_customers) - - `actor_type` - > Type of the cart creator. + - `id`: PK + FK. + - `kind` + > What kind of funnel is it? > - > - customer - > - seller - > - administrator - - `created_at`: Creation time of record. - - `deleted_at`: Deletion time of record. + > - path + > - referrer + > - variable + - `key`: Key name of funnel, when `kind` is "variable". + - `value`: Value of funnel. -### `shopping_cart_commodities` -Item in a shopping cart. +### `shopping_coupon_tickets` +Discount coupon ticket issuance details. -`shopping_cart_commodities` is an entity that represents a -[snapshot](#shopping_sale_snapshots) of the items that -[customer](#shopping_customers) has placed into his -[shopping cart](#shopping_carts) with a -[purchase](#shopping_orders) in mind. And if the customer continues -this into an actual [order](#shopping_orders) in the future, -`shopping_cart_commodities` be changed to [shopping_order_goods](#shopping_order_goods). +`shopping_coupon_tickets` is an entity that symbolizes +[discount coupon](#shopping_coupons) tickets issued by customers. -And while adding a sale snapshot to the shopping cart, the customer -inevitably selects specific [units](#shopping_sale_snapshot_units) and -[final stocks](#shopping_sale_snapshot_unit_stocks) within the listing -snapshot. Information about these units and stocks is recorded in the -subsidiary entity [shopping_cart_commodity_stocks](#shopping_cart_commodity_stocks). Also, there is an -attribute `volume` that indicates how many sets of snapshots of the -target commodity will be purchased. This "volume" is a value that will be -multiplied by [shopping_cart_commodity_stocks.quantity](#shopping_cart_commodity_stocks), the quantity -for each component. +And if the target discount coupon specification itself has an expiration +date, the expiration date is recorded in `expired_at` and is automatically +discarded after that expiration date. Of course, it doesn't matter if you +use the discount coupon for your order within the deadline. **Properties** - `id`: Primary Key. - - `shopping_cart_id`: Belonged cart's [shopping_carts.id](#shopping_carts) - - `shopping_sale_snapshot_id`: Target snapshot's [shopping_sale_snapshots.id](#shopping_sale_snapshots) - - `volume` - > Volume count. + - `shopping_customer_id`: Belonged customer's [shopping_customers.id](#shopping_customers) + - `shopping_coupon_id`: Belonged coupon's [shopping_coupons.id](#shopping_coupons) + - `shopping_coupon_disposable_id` + > Belonged disposable's [shopping_coupon_disposables.id](#shopping_coupon_disposables) > - > The value multiplied to [shopping_cart_commodity_stocks.quantity](#shopping_cart_commodity_stocks). + > Only when current ticket be issued from one-time code. - `created_at`: Creation time of record. - - `published` - > Whether be published or not. - > - > Is current commodity ordered and be paid? - > - > Until purchase the commodity, the commodity can be reused to create new - > cart commodity. This variable can be computed by referencing - > [order](#Orders) related tables, but just denormalized for the - > performance reason. - -### `shopping_cart_commodity_stocks` -Final stock information of commodity added to the shopping cart. + - `expired_at`: Expiration time of ticket. -`shopping_cart_commodity_stocks` is a subsidiary entity of -[shopping_cart_commodities](#shopping_cart_commodities) that embodies the information of the -[snapshot](#shopping_sale_snapshots) of the items in the shopping cart, -and is a concept that corresponds to the individual -[units](#shopping_sale_snapshot_units) in the target item snapshot -and the [stock](#shopping_sale_snapshot_unit_stocks) finally selected -among those [units](#shopping_sale_snapshot_units). +### `shopping_coupon_ticket_payments` +Discount coupon ticket payment details. -Therefore, if the customer selects multiple units and stocks from the -target sale snapshot, the attributed [shopping_cart_commodities](#shopping_cart_commodities) record -also has multiple corresponding `shopping_cart_commodity_stocks` records. +`shopping_coupon_ticket_payments` is an entity that embodies the payment +information for the [order](#shoppiing_orders) of +[shopping_coupon_tickets](#shopping_coupon_tickets), and is used when a consumer uses the +discount coupon ticket he or she was issued to order and has the payment +amount deducted. -And `shopping_cart_commodity_stocks` has a `quantity` property that indicates -how many final stocks would be purchased in total. The final quantity -actually purchased can be multiplied by the -[shopping_cart_commodities.volume](#shopping_cart_commodities) value of the parent entity. +And since [shopping_orders](#shopping_orders) itself is not an entity used in situations +where an order is completed, but rather an entity designed to express an +order request, the creation of this `shopping_coupon_ticket_payments` record +does not actually mean that the attached ticket disappears. Until the +customer completes the payment and confirms the order, the ticket can be +understood as a kind of deposit. + +Additionally, this record can be deleted by the customer reversing the +payment of the ticket, but it can also be deleted when the attribution +order itself is cancelled. **Properties** - `id`: Primary Key. - - `shopping_cart_commodity_id`: Belonged commodity's [shopping_cart_commodities.id](#shopping_cart_commodities) - - `shopping_sale_snapshot_unit_id`: Target unit's [shopping_sale_snapshot_units.id](#shopping_sale_snapshot_units) - - `shopping_sale_snapshot_unit_stock_id`: Target final stock's [shopping_sale_snapshot_unit_stocks.id](#shopping_sale_snapshot_unit_stocks) - - `quantity`: Quantity count. - - `sequence`: Sequence order in belonged cart commodity. + - `shopping_coupon_ticket_id`: Belonged ticket's [shopping_coupon_tickets.id](#shopping_coupon_tickets) + - `shopping_order_id`: Target order's [shopping_orders.id](#shopping_orders) + - `sequence`: Sequence order(?) in belonged order. + - `created_at`: Creation time of record. + - `deleted_at` + > Deletion time of record. + > + > In other words, it means that the target order be erased or payment + > be cancelled. -### `shopping_cart_commodity_stock_choices` -Option choice information for the final stock added to the shopping cart. +### `shopping_coupon_disposables` +Discount coupon issuance code management. -`shopping_cart_commodity_stock_choices` is a subsidiary entity of -[shopping_cart_commodity_stocks](#shopping_cart_commodity_stocks). It records which -[options](#shopping_sale_snapshot_unit_options) the customer -specifically used while putting a specific -[unit](#shopping_sale_snapshot_units) and specific -[stock](#shopping_sale_snapshot_unit_stocks) of the -[sale snapshot](#shopping_sale_snapshots) in the shopping cart, and -which [candidate values](#shopping_sale_snapshot_unit_option_candidates) -were selected or written within the shopping cart. +If a [discount coupon](#shopping_coupons) is not public and anyone can +receive the ticket, but can only be received by entering a specific +password (one-time code), use this `shopping_coupon_disposables` entity. -Therefore, `shopping_cart_commodity_stock_choices` has reference properties -and predicate properties for candidate values in addition to references -to options. If the `type` of target option is "select", enter the -candidate value selected by the customer. Otherwise, enter the value -written by the customer. +I repeat again, the code code is "one-time use". In other words, if any of +the customers enters the code, the code is discarded when the ticket +issuance to the customer is completed. Therefore, if you want to issue +tickets multiple times using a discount coupon as a secret code, the +issuing code must also be supported by the corresponding quantity. **Properties** - `id`: Primary Key. - - `shopping_cart_commodity_stock_id`: Belonged cart-commodity-stock's [shopping_cart_commodity_stocks.id](#shopping_cart_commodity_stocks) - - `shopping_sale_snapshot_unit_option_id`: Target option's [shopping_sale_snapshot_unit_options.id](#shopping_sale_snapshot_unit_options) - - `shopping_sale_snapshot_unit_option_candidate_id`: Selected candidate's [shopping_sale_snapshot_unit_option_candidates.id](#shopping_sale_snapshot_unit_option_candidates) - - `value`: User-written value for descriptive option. - - `sequence`: Sequence order in belonged cart-commodity-stock. + - `shopping_coupon_id`: Belonged coupon's [shopping_coupons.id](#shopping_coupons) + - `code` + > Identifier code. + > + > Another word, one-time password for issuance. + - `created_at`: Creation time of record. + - `expired_at`: Expired time of record. -## Coupons +## Coins ```mermaid erDiagram -"shopping_coupons" { +"shopping_deposits" { String id PK - String shopping_customer_id FK - String actor_type - String name - String access - Boolean exclusive - String unit - Float value - Float threshold "nullable" - Int limit "nullable" - Int volume "nullable" - Int volume_per_citizen "nullable" - Int expired_in "nullable" - DateTime expired_at "nullable" - DateTime opened_at "nullable" - DateTime closed_at "nullable" + String code UK + String source + Int direction DateTime created_at - DateTime updated_at DateTime deleted_at "nullable" } -"shopping_coupon_criterias" { - String id PK - String shopping_coupon_id FK - String type - String direction - Int sequence -} -"shopping_coupon_section_criterias" { - String id PK - String shopping_section_id FK -} -"shopping_coupon_channel_criterias" { +"shopping_deposit_histories" { String id PK - String shopping_channel_id FK - String shopping_channel_category_id FK "nullable" + String shopping_deposit_id FK + String shopping_citizen_id FK + String source_id + Float value + DateTime created_at + DateTime cancelled_at "nullable" } -"shopping_coupon_seller_criterias" { +"shopping_deposit_charges" { String id PK - String shopping_seller_id FK + String shopping_customer_id FK + Float amount + DateTime created_at + DateTime deleted_at "nullable" } -"shopping_coupon_sale_criterias" { +"shopping_deposit_charge_publishes" { String id PK - String shopping_sale_id FK + String shopping_deposit_charge_id FK + String password "nullable" + DateTime created_at + DateTime paid_at "nullable" + DateTime cancelled_at "nullable" } -"shopping_coupon_funnel_criterias" { +"shopping_mileages" { String id PK - String kind - String key "nullable" - String value + String code UK + String source + Int direction + Float default "nullable" + DateTime created_at + DateTime deleted_at "nullable" } -"shopping_coupon_tickets" { +"shopping_mileage_histories" { String id PK - String shopping_customer_id FK - String shopping_coupon_id FK - String shopping_coupon_disposable_id FK "nullable" + String shopping_mileage_id FK + String shopping_citizen_id FK + String source_id + Float value DateTime created_at - DateTime expired_at "nullable" + DateTime cancelled_at "nullable" } -"shopping_coupon_ticket_payments" { +"shopping_customers" { String id PK - String shopping_coupon_ticket_id FK - String shopping_order_id FK - Int sequence + String shopping_channel_id FK + String shopping_member_id FK "nullable" + String shopping_external_user_id FK "nullable" + String shopping_citizen_id FK "nullable" + String href + String referrer + String ip DateTime created_at - DateTime deleted_at "nullable" } -"shopping_coupon_disposables" { +"shopping_citizens" { String id PK - String shopping_coupon_id FK - String code UK + String shopping_channel_id FK "nullable" + String mobile + String name DateTime created_at - DateTime expired_at "nullable" + DateTime deleted_at "nullable" } -"shopping_coupon_criterias" }|--|| "shopping_coupons" : coupon -"shopping_coupon_section_criterias" |o--|| "shopping_coupon_criterias" : base -"shopping_coupon_channel_criterias" |o--|| "shopping_coupon_criterias" : base -"shopping_coupon_seller_criterias" |o--|| "shopping_coupon_criterias" : base -"shopping_coupon_sale_criterias" |o--|| "shopping_coupon_criterias" : base -"shopping_coupon_funnel_criterias" |o--|| "shopping_coupon_criterias" : base -"shopping_coupon_tickets" }|--|| "shopping_coupons" : coupon -"shopping_coupon_tickets" |o--|| "shopping_coupon_disposables" : disposable -"shopping_coupon_ticket_payments" |o--|| "shopping_coupon_tickets" : ticket -"shopping_coupon_disposables" }|--|| "shopping_coupons" : coupon +"shopping_deposit_histories" }|--|| "shopping_deposits" : deposit +"shopping_deposit_histories" }|--|| "shopping_citizens" : citizen +"shopping_deposit_charges" }|--|| "shopping_customers" : customer +"shopping_deposit_charge_publishes" |o--|| "shopping_deposit_charges" : charge +"shopping_mileage_histories" }|--|| "shopping_mileages" : mileage +"shopping_mileage_histories" }|--|| "shopping_citizens" : citizen +"shopping_customers" }o--|| "shopping_citizens" : citizen ``` -### `shopping_coupons` -Discount coupon. +### `shopping_deposits` +Meta information of the deposit. -`shopping_coupons` is an entity that symbolizes discount coupons at a -shopping mall. +`shopping_deposits` is an entity that embodies the specifications for +incomes and outcomes at a shopping mall. In other words, +`shopping_deposits` is not [shopping_deposit_histories](#shopping_deposit_histories), which +refers to the deposit/outcome details of deposits, but is simply +metadata that specifies specifications for income/outcome scenarios. -Note that, `shopping_coupons` only contains specification information -about discount coupons. Please keep in mind that this is a different -concept from [shopping_coupon_tickets](#shopping_coupon_tickets), which refers to the issuance -of a discount coupon, or [shopping_coupon_ticket_payments](#shopping_coupon_ticket_payments), which -refers to its payment. +**Properties** + - `id`: Primary Key. + - `code`: Identifier code. + - `source`: The source table occuring the deposit event. + - `direction` + > Direction of deposit. + > + > - `1`: Income + > - `-1`: outcome + - `created_at`: Creation time of record. + - `deleted_at`: / Deletion time of record. -Additionally, discount coupons are applied on an -[order-by-order](#shopping_orders) basis, but each has its own unique -restrictions. For example, a coupon with -[shopping_coupon_seller_criterias](#shopping_coupon_seller_criterias) may or may not be used only for -[snapshots](#shopping_sale_snapshots) of listings registered by the -[seller](#shopping_sellers). Also, there are restrictions such as -minimum amount restrictions for using discount coupons and maximum discount -amount limits. +### `shopping_deposit_histories` +Deposit income/outcome details of customers (citizens). + +`shopping_deposit_histories` is an entity that embodies the +[customer](#shopping_customers)'s income/outcome history. + +You can think of it as a kind of accounting ledger table. Therefore, +note that, `value` must have positive number only, even if it is a +outcome. The minus value must be expressed by multiplying the +[shopping_deposits.direction](#shopping_deposits) value of the corresponding. + +**Properties** + - `id`: Primary Key. + - `shopping_deposit_id`: Belonged metadata's [shopping_deposits.id](#shopping_deposits) + - `shopping_citizen_id`: Belonged citizen's [shopping_citizens.id](#shopping_citizens) + - `source_id`: The source record occured deposit/outcome. + - `value` + > Income/outcome amount of deposit. + > + > It must be a positive number, and you can check + > [shopping_deposits.direction](#shopping_deposits) for incomes and outcomes. + > If you want to express the figures for incomes and outcomes as + > positive/negative numbers, you can also multiply this field value by + > the attributed [shopping_deposits.direction](#shopping_deposits) value. + - `created_at`: Creation time of record. + - `cancelled_at`: Cancelled time of record. -In addition, you can set whether to issue discount coupons publicly or give -them only to people who know the specific issuing code. In addition, there -are restrictions such as issued discount coupons having an expiration date -or being issued only to customers who came in through a specific channel. +### `shopping_deposit_charges` +Deposit deposit. -For more information, please refer to the properties below and the -subsidiary entities described later. +`shopping_deposit_charges` is an entity that symbolizes the act of a +[customer](#shopping_customers) applying for a deposit to a shopping +mall. + +However, `shopping_deposit_charges` expresses the customer's intention to +make a deposit, but it has not yet been confirmed. Only when the customer +completes the [payment](#shopping_deposit_charge_publishes) +will the deposit increase be confirmed. **Properties** - `id`: Primary Key. - - `shopping_customer_id`: Belonged administrator or seller's [shopping_customers.id](#shopping_customers) - - `actor_type` - > Type of the coupon creator. - > - > - administrator - > - seller - - `name`: Reprensentative name of coupon. - - `access` - > Access level of coupon. - > - > - `public`: possible to find from public API - > - `private`: unable to find from public API - > - arbitrarily assigned by the seller or administrator - > - issued from one-time link - - `exclusive` - > Exclusivity or not. - > - > An exclusive discount coupon refers to a discount coupon that has an - > exclusive relationship with other discount coupons and can only be used - > alone. That is, when an exclusive discount coupon is used, no other - > discount coupon can be used for the same [order](#shopping_orders) - > or [good](#shopping_order_goods). - > - > Please note that this `exclusive` attribute is a very different - > concept from `multiplicative`, which means whether the same coupon - > can be multiplied and applied to multiple coupons of the same order, - > so please do not confuse them. - - `unit` - > Discount unit. - > - > - amount: Absolute value - > - percent: 0 ~ 100 % - - `value` - > Discount value. - > - > If `unit` is percent, range of value is limited from 0 to 100. - - `threshold` - > Minimum purchase amount for discount. - > - > When setting this value, discount coupons cannot be applied to - > order totals that are less than this value. - - `limit` - > Maximum amount available for discount. - > - > When this value is set, no further discount will be given no matter - > how much you order. - - `volume` - > Limited quantity issued. - > - > If there is a limit to the quantity issued, it becomes impossible to - > issue tickets exceeding this value. - > - > In other words, the concept of N coupons being issued on a first-come, - > first-served basis is created. - - `volume_per_citizen` - > Limited quantity issued per person. - > - > As a limit to the total amount of issuance per person, it is common to - > assign 1 to limit duplicate issuance to the same citizen, or to use - > the `NULL` value to set no limit. - > - > Of course, by assigning a value of N, the total amount issued to the - > same citizen can be limited. - - `expired_in` - > Expiration day(s) value. - > - > The concept of expiring N days after a discount coupon ticket is issued. - > - > Therefore, customers must use the ticket within N days, if possible, - > from the time it is issued. - - `expired_at` - > Expiration date. - > - > A concept that expires after YYYY-MM-DD after a discount coupon ticket - > is issued. - > - > Double restrictions are possible with `expired_in`, of which the one - > with the shorter expiration date is used. - - `opened_at`: Issuance starting date. - - `closed_at` - > Issuance end date. - > - > Tickets cannot be issued after this time. - > - > However, previously issued tickets can still be used until their - > expiration date. + - `shopping_customer_id`: Belonged metadata's [shopping_deposits.id](#shopping_deposits) + - `amount`: Charging amount. - `created_at`: Creation time of record. - - `updated_at` - > Update time of record. - > - > Only possible to update until `opened_at`. - `deleted_at` > Deletion time of record. > - > Pre-issued tickets can still be used until their expiration date. + > Only when be stopped before publishing. -### `shopping_coupon_criterias` -Supertype for the applicable conditions of the discount coupon. +### `shopping_deposit_charge_publishes` +Payment progress information for deposits. -`shopping_coupon_criterias` is a supertype entity that embodies the -conditions for applying a [discount coupon](#shopping_coupons). All -subtype entities that wish to impose constraints on the reference unit of -a discount coupon were created by inheriting this. For example, the -[shopping_coupon_section_criterias](#shopping_coupon_section_criterias) entity, designed to limit -application to a specific [section](#shopping_sections), inherits this -entity `shopping_coupon_criterias`. +`shopping_deposit_charge_publishes` is an entity that embodies the process +of a [customer](#shopping_customers) applying for a deposit and making +a payment. -In addition, constraints on reference units can be specified through the -`direction` property to proceed as an inclusion condition or, conversely, -as an exclusion condition. If the direction value is "include", the coupon -is applicable only to the reference object. Conversely, if the direction -value is "exclude", it is a coupon that cannot be applied to the reference -object. +Please note that the existence of the `shopping_deposit_charge_publishes` +record does not mean that payment has been completed. Payment is complete +only when payment (`paid_at`) is complete. This is what the +"process of payment" mentioned above means. + +However, even after payment has been made, there may be cases where it is +suddenly cancelled, so you must be careful about this as well. **Properties** - `id`: Primary Key. - - `shopping_coupon_id`: Belonged coupon's [shopping_coupons.id](#shopping_coupons) - - `type` - > Type of criteria. + - `shopping_deposit_charge_id`: Belonged charge appliance's [shopping_deposit_charges.id](#shopping_deposit_charges) + - `password` + > Password for encryption. > - > It means which subtype being used. - - `direction` - > Direction of criteria. + > This shopping mall system uses a randomly issued password to encrypt + > payment history, and is completely unrelated to the user. + - `created_at` + > Creation time of record. > - > - include - > - exclude - - `sequence`: Sequence order in belonged coupon. - -### `shopping_coupon_section_criterias` -Conditions for sections of discount coupons. + > Note that, this property does not mean the payment completion time. + - `paid_at` + > Completion time of payment. + > + > This property is the only way to know if the payment has been + > completed. If this property is null, the payment has not been + > completed yet. + - `cancelled_at`: The time when the payment was cancelled or reverted. -`shopping_coupon_section_criterias` is a subtype entity of -[shopping_coupon_criterias](#shopping_coupon_criterias) and is used when setting conditions for -a specific [section](#shopping_sections). +### `shopping_mileages` +Meta information of mileage. -If the [shopping_coupon_criterias.direction](#shopping_coupon_criterias) value is "include", -the coupon can only be used for that section. Conversely, if it is -"exclude", the coupon cannot be used. And if there are multiple -`shopping_coupon_section_criterias` records within one coupon, conditions -are applied on a bundle basis. Coupons may or may not be applicable to -eligible sections. +`shopping_mileages` is an entity that embodies specifications for mileage +deposits and outcomes at a shopping mall. In other words, +`shopping_mileages` is not [shopping_mileage_histories](#shopping_mileage_histories), which means +mileage deposit and outcome history, but is simply metadata that +specifies specifications for scenarios in which mileage is deposited and +withdrawn. **Properties** - `id`: Primary Key. - - `shopping_section_id`: Target section's [shopping_coupon_criterias.id](#shopping_coupon_criterias) + - `code`: Identifier code. + - `source`: The source table occuring the mileage event. + - `direction` + > Direction of mileage. + > + > - `1`: Income + > - `-1`: outcome + - `default` + > Default value of mileage. + > + > Possible to mit, and how to use this default value is up to the + > backend program. It is okay to use it as a default value when + > creating a new record, or percentage value to be applied. + - `created_at`: Creation time of record. + - `deleted_at`: Deletion time of record. -### `shopping_coupon_channel_criterias` -Conditions for channels of discount coupons. +### `shopping_mileage_histories` +Mileagea income/outcome details of customers (citizens). -`shopping_coupon_channel_criterias` is a subtype entity of -[shopping_coupon_criterias](#shopping_coupon_criterias) and is used when setting conditions on -a specific [channel](#shopping_channels) or -[category](#shopping_channel_categories) of that channel. +`shopping_mileage_histories` is an entity that embodies the +[customer](#shopping_customers)'s deposit/outcome history. -If the [shopping_coupon_criterias.direction](#shopping_coupon_criterias) value is "include", -the coupon can only be used for that channel (or category). Conversely, -if it is "exclude", it is a coupon that cannot be used. And if there are -multiple `shopping_coupon_channel_criterias` records within one coupon, -conditions are applied on a bundle basis. Coupons may or may not be -applicable for target channels and categories. +You can think of it as a kind of accounting ledger table. Therefore, +note that, `value` must have positive number only, even if it is a +outcome. The minus value must be expressed by multiplying the +[shopping_mileages.direction](#shopping_mileages) value of the corresponding. **Properties** - `id`: Primary Key. - - `shopping_channel_id`: Target channel's [shopping_channels.id](#shopping_channels) - - `shopping_channel_category_id`: Target channel category's [shopping_channel_categories.id](#shopping_channel_categories) + - `shopping_mileage_id`: Belonged metadata's [shopping_mileages.id](#shopping_mileages) + - `shopping_citizen_id`: Belonged citizen's [shopping_citizens.id](#shopping_citizens) + - `source_id`: The source record occured income/outcome. + - `value` + > Income/outcome amount of mileage. + > + > It must be a positive number, and you can check + > [shopping_mileages.direction](#shopping_mileages) for incomes and outcomes. + > If you want to express the figures for incomes and outcomes as + > positive/negative numbers, you can also multiply this field value by + > the attributed [shopping_mileages.direction](#shopping_mileages) value. + - `created_at`: Creation time of record. + - `cancelled_at`: Cancelled time of record. + + +## Inquiries +```mermaid +erDiagram +"shopping_sale_snapshot_inquiries" { + String id PK + String shopping_sale_id FK + String shopping_sale_snapshot_id FK + String shopping_customer_id FK + String type + DateTime created_at + DateTime read_by_seller_at "nullable" +} +"shopping_sale_snapshot_questions" { + String id PK + Boolean secret +} +"shopping_sale_snapshot_reviews" { + String id PK + String shopping_order_good_id FK +} +"shopping_sale_snapshot_review_snapshots" { + String id PK + Float score +} +"shopping_sale_snapshot_inquiry_answers" { + String id PK + String shopping_sale_snapshot_inquiry_id FK + String shopping_seller_customer_id FK +} +"shopping_sale_snapshot_inquiry_comments" { + String id PK + String shopping_customer_id FK + String actor_type +} +"bbs_articles" { + String id PK + DateTime created_at + DateTime deleted_at "nullable" +} +"bbs_article_snapshots" { + String id PK + String bbs_article_id FK + String format + String title + String body + DateTime created_at +} +"bbs_article_comments" { + String id PK + String bbs_article_id FK + String parent_id FK "nullable" + DateTime created_at + DateTime deleted_at "nullable" +} +"shopping_sale_snapshots" { + String id PK + String shopping_sale_id FK + DateTime created_at +} +"shopping_sale_snapshot_inquiries" ||--|| "bbs_articles" : base +"shopping_sale_snapshot_inquiries" }|--|| "shopping_sale_snapshots" : snapshot +"shopping_sale_snapshot_questions" |o--|| "shopping_sale_snapshot_inquiries" : base +"shopping_sale_snapshot_reviews" |o--|| "shopping_sale_snapshot_inquiries" : base +"shopping_sale_snapshot_review_snapshots" ||--|| "bbs_article_snapshots" : base +"shopping_sale_snapshot_inquiry_answers" ||--|| "bbs_articles" : base +"shopping_sale_snapshot_inquiry_answers" |o--|| "shopping_sale_snapshot_inquiries" : inquiry +"shopping_sale_snapshot_inquiry_comments" ||--|| "bbs_article_comments" : base +"bbs_article_snapshots" }|--|| "bbs_articles" : article +"bbs_article_comments" }|--|| "bbs_articles" : article +"bbs_article_comments" }o--o| "bbs_article_comments" : parent +``` -### `shopping_coupon_seller_criterias` -Conditions for sellers of discount coupons. +### `shopping_sale_snapshot_inquiries` +Inquiry about a sale snapshot. -`shopping_coupon_seller_criterias` is a subtype entity of -[shopping_coupon_criterias](#shopping_coupon_criterias) and is used when setting conditions for -a specific [seller](#shopping_sellers). +`shopping_sale_snapshot_inquiries` is a subtype entity of +[bbs_articles](#bbs_articles), and represents inquiries written by +[customers](#shopping_customers) about a [sale](#shopping_sales) +registered by the [seller](#shopping_sellers) (however, to trace the +exact [snapshot](#shopping_sale_snapshots), it is referencing not +sale but snapshot). -If the [shopping_coupon_criterias.direction](#shopping_coupon_criterias) value is "include", the -coupon can only be used for that seller. Conversely, if it is "exclude", -the coupon cannot be used. +In addition, since the customer is waiting for the seller's response after +writing the inquiry, whether the seller has viewed the inquiry written by +the customer is provided for reference as `read_by_seller_at` property. +Of course, since the inquiry itself is a subtype of a article, it is also +possible for sellers to communicate with each other through +[comments](#shopping_sale_snapshot_inquiry_comments) before an +[official response](#shopping_sale_snapshot_inquiry_responses). -And if there are multiple `shopping_coupon_seller_criterias` records within -one coupon, conditions are applied on a bundle basis. Coupons may or may -not be applicable to eligible sellers. +However, comments themselves can be made by every customers, even if they +are not the person who wrote the article. Of course, it cannot be written +unless the seller is a party. **Properties** - `id`: PK + FK. - - `shopping_seller_id`: Target seller's [shopping_sellers.id](#shopping_sellers) - -### `shopping_coupon_sale_criterias` -Conditions for a specific item in a discount coupon. + - `shopping_sale_id` + > Belonged sale's [shopping_sales.id](#shopping_sales) + > + > Duplicated property for fast sorting. + - `shopping_sale_snapshot_id`: Belonged snapshot's [shopping_sale_snapshots.id](#shopping_sale_snapshots) + - `shopping_customer_id`: Writer customer's [shopping_customers.id](#shopping_customers) + - `type` + > Type of the inquiry article. + > + > - `question` + > - `review` + - `created_at` + > Creation time of record. + > + > Duplicated property for fast sorting. + - `read_by_seller_at`: The first time when the seller read the inquiry. -`shopping_coupon_sale_criterias` is a subtype entity of -[shopping_coupon_criterias](#shopping_coupon_criterias) and is used when setting conditions for -a specific [sale](#shopping_sales). +### `shopping_sale_snapshot_questions` +Question about sale snapshot. -If the [shopping_coupon_criterias.direction](#shopping_coupon_criterias) value is "include", -the coupon can only be used for that item. Conversely, if it is "exclude", -it is a coupon that cannot be used. +`shopping_sale_snapshot_questions` is a subtype entity of +[shopping_sale_snapshot_inquiries](#shopping_sale_snapshot_inquiries), and is used when a +[customer](#shopping_customers) wants to ask something about a +sale ([snapshot](#shopping_sale_snapshots) at the time) registered by +the [seller](#shopping_sellers). -And if there are multiple shopping_coupon_sale_criterias records within one coupon, conditions are applied on a bundle basis. Coupons that may or may not be applicable to target properties. +And, like most shopping malls, `shopping_sale_snapshot_questions` also +provides a `secret` attribute, allowing you to create a "secret message" +that can only be viewed by the seller and the customer who wrote the +question. **Properties** - `id`: PK + FK. - - `shopping_sale_id`: Target sale's [shopping_sales.id](#shopping_sales) + - `secret` + > Whether secret or not. + > + > If secret article, only the writer customer and related seller can see + > the detailed content. -### `shopping_coupon_funnel_criterias` -Limit the funnel of discount coupons. +### `shopping_sale_snapshot_reviews` +Reviews for sale snapshots. -`shopping_coupon_funnel_criterias` is a subtype entity of -[shopping_coupon_criterias](#shopping_coupon_criterias), and is used when you want to issue or -exclude discount coupons only to [customers](#shopping_customers) who -came from a specific path. +`shopping_sale_snapshot_reviews` is a subtype entity of +[shopping_sale_snapshot_inquiries](#shopping_sale_snapshot_inquiries), and is used when a +[customer](#shopping_customers) purchases a sale +([snapshot](#shopping_sale_snapshots) at the time) registered by the +[seller](#shopping_sellers) as a product and leaves a review and +rating for it. -And funnel restrictions are possible in 3 ways: The first is -[shopping_customers.referrer](#shopping_customers), and by parsing -[shopping_customers.href](#shopping_customers), which records the customer's access -address, restrictions can be made in units of specific URLs or variables. +For reference, `shopping_sale_snapshot_reviews` and +[shopping_order_goods](#shopping_order_goods) have a logarithmic relationship of N: 1, +but this does not mean that customers can continue to write reviews for +the same product indefinitely. Wouldn't there be restrictions, such as +if you write a review once, you can write an additional review a month +later? **Properties** - `id`: PK + FK. - - `kind` - > What kind of funnel is it? - > - > - path - > - referrer - > - variable - - `key`: Key name of funnel, when `kind` is "variable". - - `value`: Value of funnel. + - `shopping_order_good_id`: Belonged good's [shopping_order_goods.id](#shopping_order_goods) -### `shopping_coupon_tickets` -Discount coupon ticket issuance details. +### `shopping_sale_snapshot_review_snapshots` +A snapshot of the content of the review for the sale snapshot. -`shopping_coupon_tickets` is an entity that symbolizes -[discount coupon](#shopping_coupons) tickets issued by customers. +`shopping_sale_snapshot_review_snapshots` is a subtype entity of +[bbs_article_snapshots](#bbs_article_snapshots) and is designed to add a `score` property +to the content of [review article](#shopping_sale_snapshot_reviews). -And if the target discount coupon specification itself has an expiration -date, the expiration date is recorded in `expired_at` and is automatically -discarded after that expiration date. Of course, it doesn't matter if you -use the discount coupon for your order within the deadline. +In other words, after writing a review article, customers can edit it +and change the evaluation `score` at any time. **Properties** - - `id`: Primary Key. - - `shopping_customer_id`: Belonged customer's [shopping_customers.id](#shopping_customers) - - `shopping_coupon_id`: Belonged coupon's [shopping_coupons.id](#shopping_coupons) - - `shopping_coupon_disposable_id` - > Belonged disposable's [shopping_coupon_disposables.id](#shopping_coupon_disposables) - > - > Only when current ticket be issued from one-time code. - - `created_at`: Creation time of record. - - `expired_at`: Expiration time of ticket. + - `id`: PK + FK. + - `score`: Estimation score value. -### `shopping_coupon_ticket_payments` -Discount coupon ticket payment details. +### `shopping_sale_snapshot_inquiry_answers` +Answers to questions about sale snapshots. -`shopping_coupon_ticket_payments` is an entity that embodies the payment -information for the [order](#shoppiing_orders) of -[shopping_coupon_tickets](#shopping_coupon_tickets), and is used when a consumer uses the -discount coupon ticket he or she was issued to order and has the payment -amount deducted. +`shopping_sale_snapshot_inquiry_answers` is an entity that embodies the +official answer written by the [seller](#shopping_sellers) to the +[inquiry](#shopping_sale_snapshot_inquiries) written by the +[customer](#shopping_customers). -And since [shopping_orders](#shopping_orders) itself is not an entity used in situations -where an order is completed, but rather an entity designed to express an -order request, the creation of this `shopping_coupon_ticket_payments` record -does not actually mean that the attached ticket disappears. Until the -customer completes the payment and confirms the order, the ticket can be -understood as a kind of deposit. +Of course, in addition to writing an official response like this, it is +also possible for the seller to communicate with the questioner and +multiple customers through +[comments](#shopping_sale_snapshot_inquiry_comments) in the +attribution inquiry. -Additionally, this record can be deleted by the customer reversing the -payment of the ticket, but it can also be deleted when the attribution -order itself is cancelled. +For refererence, it is not possible to write comments on this answer. +Encourage people to write comments on the inquiry article. This is to +prevent comments from being scattered in both inquiry and response +articles. **Properties** - - `id`: Primary Key. - - `shopping_coupon_ticket_id`: Belonged ticket's [shopping_coupon_tickets.id](#shopping_coupon_tickets) - - `shopping_order_id`: Target order's [shopping_orders.id](#shopping_orders) - - `sequence`: Sequence order(?) in belonged order. - - `created_at`: Creation time of record. - - `deleted_at` - > Deletion time of record. - > - > In other words, it means that the target order be erased or payment - > be cancelled. + - `id`: PK + FK + - `shopping_sale_snapshot_inquiry_id`: Belonged inquiry's [shopping_sale_snapshot_inquiries.id](#shopping_sale_snapshot_inquiries) + - `shopping_seller_customer_id`: Answered seller's [shopping_customers.id](#shopping_customers) -### `shopping_coupon_disposables` -Discount coupon issuance code management. +### `shopping_sale_snapshot_inquiry_comments` +A comment written on an inquiry article. -If a [discount coupon](#shopping_coupons) is not public and anyone can -receive the ticket, but can only be received by entering a specific -password (one-time code), use this `shopping_coupon_disposables` entity. +`shopping_sale_snapshot_inquiry_comments` is a subtype entity of +[bbs_article_comments](#bbs_article_comments), and is used when you want to communicate with +multiple people about an [inquiry](#shopping_sale_snapshot_inquiries) +written by a [customer](#shopping_customers). -I repeat again, the code code is "one-time use". In other words, if any of -the customers enters the code, the code is discarded when the ticket -issuance to the customer is completed. Therefore, if you want to issue -tickets multiple times using a discount coupon as a secret code, the -issuing code must also be supported by the corresponding quantity. +For reference, only related parties can write comments for +[sellers](#shopping_sellers), but there is no limit to customers. +In other words, anyone customer can freely write a comment, even if they are +not the person who wrote the inquiry. **Properties** - - `id`: Primary Key. - - `shopping_coupon_id`: Belonged coupon's [shopping_coupons.id](#shopping_coupons) - - `code` - > Identifier code. + - `id`: PK + FK + - `shopping_customer_id`: Writer's [shopping_customers.id](#shopping_customers) + - `actor_type` + > Type of the writer. > - > Another word, one-time password for issuance. - - `created_at`: Creation time of record. - - `expired_at`: Expired time of record. \ No newline at end of file + > - customer + > - seller \ No newline at end of file diff --git a/package.json b/package.json index 3e6a729..047af58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prisma-markdown", - "version": "1.0.5", + "version": "1.0.6", "description": "Prisma Markdown documents generator including ERD diagrams and comment descriptions", "main": "lib/index.js", "typings": "lib/index.d.ts", diff --git a/src/writers/MarkdownWriter.ts b/src/writers/MarkdownWriter.ts index edec2b7..2c762c3 100644 --- a/src/writers/MarkdownWriter.ts +++ b/src/writers/MarkdownWriter.ts @@ -16,53 +16,79 @@ export namespace MarkdownWriter { ); findImplicits(modelList); + const emplace = (name: string) => + MapUtil.take(dict)(name, () => ({ + name, + descriptions: new Set(), + diagrams: new Set(), + })); + + // TOP NAMESPACE for (const model of modelList) { const namespaces: string[] = takeTags("namespace")(model); - const describes: string[] = takeTags("describe")(model); - const erdList: string[] = takeTags("erd")(model); + if (namespaces.length === 0) continue; - if ( - namespaces.length === 0 && - describes.length === 0 && - erdList.length === 0 - ) { - const basic = MapUtil.take(dict)("default", () => ({ - name: "default", - descriptions: new Set(), - diagrams: new Set(), - })); - basic.descriptions.add(model); - basic.diagrams.add(model); - continue; - } + const top: string = namespaces[0]; + const chapter: IChapter = emplace(top); + chapter.descriptions.add(model); + chapter.diagrams.add(model); + } - for (const name of namespaces) { - const section = MapUtil.take(dict)(name, () => ({ - name, - descriptions: new Set(), - diagrams: new Set(), - })); + // REMAINING NAMESPACES + for (const model of modelList) { + const namespaces: string[] = takeTags("namespace")(model); + for (const name of namespaces.slice(1)) { + const section = emplace(name); section.descriptions.add(model); section.diagrams.add(model); } + } + + // DESCRIPTIONS + for (const model of modelList) { + const describes: string[] = takeTags("describe")(model); for (const name of describes) { - const section = MapUtil.take(dict)(name, () => ({ + const chapter: IChapter = MapUtil.take(dict)(name, () => ({ name, descriptions: new Set(), diagrams: new Set(), })); - section.descriptions.add(model); + chapter.descriptions.add(model); } + } + + // ERD ONLY + for (const model of modelList) { + const erdList: string[] = takeTags("erd")(model); for (const erd of erdList) { - const section = MapUtil.take(dict)(erd, () => ({ + const chapter: IChapter = MapUtil.take(dict)(erd, () => ({ name: erd, descriptions: new Set(), diagrams: new Set(), })); - section.diagrams.add(model); + chapter.diagrams.add(model); } } + // DEFAULTS + for (const model of modelList) { + const keywords: string[] = [ + ...takeTags("namespace")(model), + ...takeTags("describe")(model), + ...takeTags("erd")(model), + ]; + if (keywords.length !== 0) continue; + + const basic: IChapter = MapUtil.take(dict)("default", () => ({ + name: "default", + descriptions: new Set(), + diagrams: new Set(), + })); + basic.descriptions.add(model); + basic.diagrams.add(model); + } + + // DO WRITE const title: string = typeof config?.title === "string" ? config.title