Data Modeling for Ordering A "Package of Packages" filled with Items - database

Imagine an ecommerce platform that works as follows:
A Package, consists of a mixture of Items and other Packages.
An Order consists of a mixture of Items and Packages.
A user can select some items, and some packages to order, but can also go ahead and remove some items from a package as part of that order, this doesn't affect the original package.
A Package can change in the future, but the Order must show what was actually ordered as part of the packages at the time.
Here is an example:
Package_A: [Large_Knife, Cutting_Board]
Package_B: [Cheese, Package_A]
Order_1: [Wine, Small_Knife, (Package_B - Large_Knife)]
Now lets say Package_A changes to:
Package_A: [Large_Knife, Cork_Screw]
Which means the new Package_B has a Cork_Screw instead of Cutting_Board.
When the user looks at his old order he should see what he actually ordered.
Order_1: [Wine, Small_Knife, Package_B([Cheese, Cutting_Board])]
There is no need to store/show that Large_Knife was removed if that makes it easier to model.
Would love to hear ideas on how to model this.
Thanks.

An Item can be in many Packages.
A Package can contain many Items.
An Order can have many Items and Packages.
A Package or Item can be in many Orders.
A User can place many Orders.
A Order is placed by one User.
Package's items can change over time, but there´s no need to track this changes.
If a Package changes, Orders containing that Package should not be affected, and its content should remain as it was by the time it was ordered.
Proposed Model:
User: UserId(PK), LastName, FirstName, Email
Item: ItemId(PK), Description
Package: PackageId(PK), Description
PackageDetail: PackageId(FK), ItemId(FK)
Order: OrderId(PK), UserId(FK), DateOrdered
OrderDetail: OrderId(FK), ItemId(FK, not null), PackageId(FK, "0" if it's an Item)
If the Order contains an Item, field ItemId on table OrderDetail must contain its PK, and PackageId must be set to "0".
If the Order contains a Package, containing 3 items by the time it was placed, you must insert 3 rows on table OrderDetails, all with the same PackageId but with different ItemdId.

Related

Identify elements that do not appear in the period (Google Data Studio)

I have a table that shows the recurrence of purchasing a product, with the columns: product_id, report_date, quantity.
I need to list in a table the products that are more than 50 days unsold. The opposite I managed to do (list those that were sold in the last 50 days) but the opposite logic has not yet been able to implement.
Does anyone have any tips?
An example of the table:
product_id,date,report_date,quantity
329,2019-01-02 08:19:17,2019-01-02 14:34:12,6
243,2019-01-03 09:19:17,2019-01-03 15:34:12,6
238,2019-02-02 08:19:17,2019-03-02 14:34:12,84
170,2019-04-02 08:19:17,2019-04-02 14:34:12,84
238,2019-04-02 08:19:17,2019-04-02 14:34:12,8
238,2019-04-02 08:19:17,2019-04-02 14:34:12,100
238,2019-08-02 08:19:17,2019-08-02 14:34:12,100
238,2019-10-02 08:19:17,2019-10-02 14:34:12,100
170,2020-01-02 08:19:17,2020-01-02 14:34:12,84
170,2020-01-02 08:19:17,2020-01-02 14:34:12,84
There are many steps to do this task. I assume the date column is the one to work with. Your example from table includes duplicated entries. Is it right that at the same time the order is there twice?
So here are the steps:
At first add an calculated field date_past to your dataset:
DATE_DIFF(CURRENT_DATE(),date)
To the dataset add a filter SO_demo with:
include date_past<30
Then blend the data with it self. Use product_id as Join key. Only the 2nd dataset has the SO_demo filter. Add to the dimension of this dataset the calculated field sold_last_30_days with the formula "yes".
In the table/chart to display add a filter on the field include sold_last_30_days is Null.

Summing up values from one column into one row on a different table when joining

I've only had a (frustrating) couple days experience with SQL Server, and I need some help, since I cant find anything related to my question when searching.
For background info, I'm working in an accountancy, and right now I'm trying to create a table for balance statements to use in crystal reports. Each invoice has multiple items, but for the statement I need to sum them all up with the invoice reference being the information that links the group of data that needs to be summarised.
So, what I need is to pull information from one table (dbo.SalesInvItems), this information including the date (InvDate), the due date (InvDueDate), and the customer name (CustomerName). This information stays the same with the Invoice Reference (InvRef), and it's what I want to use to link the two tables.
For example, Invoice Reference: 1478 will always have the date 14/05/18, the due date: 14/06/18 and the customer name: Pen Sellers Ltd.
The same Invoice Reference is used by multiple rows, but the only thing that changes (that I need) is the Invoice Item Total (InvItemTotal).
For example, the one reference will always be addressed to Pen Sellers, but one item has the Total as £13 and another item using the same reference is £20.
The Invoice Item Total is what I need to sum up. I want to add all the Invoice Item Total's together that have the same Invoice Reference, while joining the tables.
Sales Invoices
So when I've inserted the references into the table (sorry they're not the same in both pictures, I was having problems making examples and I made a mistake), I want to grab the information from the Invoice table to fill it in, going from this...
Pre Solution
To this...
Desired Result
The desired location is a table called dbo.Statement.
1.Is this even possible to do?
2.How would I go about doing this?
3.Is there a method I could use that would make sure that every time I insert an Invoice Reference into the Statement table, it would automatically pull out the data needed from the Invoice Table?
If any additional information is needed, just say and I'll do my best to provide it, I have never asked a question on here before and I'm new to SQL Server and coding in general.
Im using SQL Server Management Tool 2017
Thank You
Select item.InvRef
, item.CustomerName
, item.InvDate
, item.InvDueDate
, Sum(item.InvItemTotal) InvAmt
, sum(IsNull(payments.Amount,0)) PayAmount
, Sum(item.InvItemTotal) InvAmt - sum(IsNull(payments.Amount,0)) as Balance
from SalesInvItems item
left join payments -- you didn't define this
on item.InvRef = payments.InvRef
group by item.InvRef
, item.CustomerName
, item.InvDate
, item.InvDueDate
Select item.InvRef
, item.CustomerName
, item.InvDate
, item.InvDueDate
, Sum(item.InvItemTotal) InvAmt
from SalesInvItems item
left join Statement
on item.InvRef = Statement.Reference
group by item.InvRef
, item.CustomerName
, item.InvDate
, item.InvDueDate

Database Structure and Querying

Trying to figure out how to change a structure from what I currently have which is this:
tblHaulLogs
intLogID
intHaulType
intSerial
intOriginSource
intOrigin
intDestinationSource
intDestination
dtmHaulDate
ccyLogPay
intHauler
txtLogNotes
intInvoiceID
In this table, what I am doing is using the origin and destination source fields to determine which table the fk for the origin and destination comes from. This feels very wrong to me.
tblHaulTypes
intHaulTypeID
chrHaulType
intOriginSourceType
intDestinationSourceType
Data in the Haul Types Table:
LOT, 1, 1
DEL, 1, 2
RPO, 2, 1
Now let me explain:
The first type happens when an item goes from a sales lot to another sales lot.
The second type happens when an item goes from a sales lot to a customer(sale gets delivered).
The third type happens when an item returns from the customer back to the sales lot.
Then the Item can be resold/returned/resold/returned(rent-to-own system).
Now, here are the problems I have:
An Haul Log's origin will always be the destination of the last move. Therefore I thought that the origin field is redundant. However, it's the relation between the destination of the last move and the destination of the new move that defines what the shipper gets paid and what type of haul it is.
In other words, even though the first type and the third type technically have the same fields, the type of move is not the same because of the previous move type. What do I need to do here? Am I totally missing the boat on what the structure should be?
The questions I need to answer based on this data is:
How many Items do I have on my sales lots that are new inventory(have never been sold).
How many Items do I have that have been sold and returned(doesn't matter how many times).
I'm guessing at the relationship between the various fields and tables.
Your tblHaulTypes table looks fine.
intHaulTypeID
chrHaulType
intOriginSourceType
intDestinationSourceType
You're missing a haul type that accounts for deliveries from suppliers to your lots.
There has to be some table that lists your lots. I'd call it tblHaulLot.
intLotNumber
txtLotName
...
I'd make a tblHaulTransaction table that looks like this.
intTransactionID
intHaulTypeID
intHauler
intOriginOrganizationID
intDestinationOrganizationID
intOriginLot (null if origin is supplier)
intDestinationLot (null if destination is customer)
dtmHaulDate
txtLogNotes
Now, we need an tblOrganization.
intOrganizationID
txtOrganizationName
txtOrganizationAddress
...
The organization at ID 0 is your organization. Suppliers and customers would fill the rest of the table.
I'd make a tblHaulInvoice table that looks like this.
intInvoiceID
intTransactionID
ccyTransactionPay
dtmDateInvoiced
AmountInvoiced
The amount invoiced (and amount paid) have to be accounted for in some table. I don't know what ccy stands for, and I don't know your 3 letter code for a decimal (money) field.
How many Items do I have on my sales lots that are new inventory(have never been sold). How many Items do I have that have been sold and returned(doesn't matter how many times).
Nowhere in your data model is there any kind of inventory table. I'd need to know a lot more about your business to create one or more inventory tables.

couchdb map / reduce multiple keys filtering by date

I have a view setup with a map reduce. Right now this code works great:
function(doc) {
if (doc.type == 'test'){
if(doc.trash != 1){
for (var id in doc.items) {
emit([id,doc.items[id].name], 1);
}
}
}
}
function(keys,prices){
return (keys, sum(prices));
}
I get a return and when using the group parameter, it condenses everything just fine.
My issue/question, I want to add a third key.... DATE, so I may only reduce records from certain dates. So for example:
function(doc) {
if (doc.type == 'test'){
if(doc.trash != 1){
for (var id in doc.items) {
emit([date,id,doc.items[id].name], 1);
}
}
}
}
My issue is that since date is at the beginning of the array, the reduce groups by date, id etc. I know I use group_level and say just take the first key from the array or the first 2 keys, but that doesn't help either because afaik, group_level goes from left to right in the array. I could put the date on the end of the emit array, but that doesn't help either because I need to have values at the beginning of my startkey and endkey to search on.
Here is an example of the output of data:
{"key":["2012-03-13","356752b8a5f6871f3","Apple"],"value":1},
{"key":["2012-03-20","123752b8a76986857","Pear"],"value":1},
{"key":["2012-04-12","3013531de05871194","Grapefruit"],"value":1},
{"key":["2012-04-12","356752b8a5f6871f3","Apple"],"value":1},
I want APPLE to be added up in one row, here it's adding up apples by date first. I was able to successfully just add up all the apples if I remove DATE as the first key in the array, but then I can't search by date range.
Any ideas on how to accomplish this?
If I correctly understand what you want to do, then you'd want to put the date as the first element of your array, and use group_level as well as start_key and end_key.
Eg. startkey=[1, "someid"] endkey=[1,"someid",{}] group_level=2
Will get you all items from date 1 (obviously choose your own format here), with id "someid" and any name. It seems funny that you emit id's before names, and without having more information about what you're actually trying to accomplish, it's hard to advise your general data model. If ID is a "type" id meaning that many items share the same ID then this makes sense. If ID is a unique per item ID, then it does not. In that case, you'd want to emit "name" before ID...
Edit 1
As per your comment, to do a range of dates you do this:
startkey=[1] endkey=[5,{}] group_level=2
You will get everything from date 1 to date 5 grouped by id ie. apples, oranges etc. I use this exact technique in a very large scale production application. I actually formatted the dates as an easily human readable integers of the format yyyymmdd, so 20140624 would sort to the top. If I want everything from the start of the month till now grouped by my group ids, I call
startkey=[20140601] endkey=[20140624,{}] group_level=2
It works perfectly and as far as I can tell that's what you're looking to do. I also have a third key layer "detail" which allows me to provide a deeper level of grouping for items that need it. I can then call
startkey=[20140601, "someid"] endkey=[20140624, "someid",{}] group_level=3
To drill to the detail level for a particular id, or just use the previous query with group_level=3 if I want the details for every id. I'm certain you can make this work - I've solved this exact problem in a production application using the techniques described.
Edit 2
If you want to group all apples regardless of date, then you'll need to let apples be the first element in the key. You can then get all apples over all time as a single row in the view result using group_level=1, and Apples over a date range using group_level=2. The difference here is that you'll only be able to do the group_level=2 query on a single item type at a time. If you want the best of both worlds, you unfortunately just need to make 2 views. That's just how key ordering works... If you need fast response times for both types of queries, all item types over a date range, and all of a particular item not grouped by date, I believe 2 views is the only way to achieve that.
Note
Another thing to note is about your reduce function. Wherever possible it is highly recommended that you use the built in reduce functions. They're implemented in erlang and are highly optimized compared to custom javascript reduce functions.
In your case, just replace your reduce function with this
_sum
Easy hey?
If you post more info about your application, data model etc. then I'd be happy to help out more with your database design.

cakephp - view with data from multiple tables as one

I want to have a view for each table (letters, filings, notes) which each have a name and date. I want to have a separate view for items from each table that also fall into a particular category -- say categoryx -- so it is a separate view. The categoryx view would combine particular fields from each of the multiple tables into one table, in effect, ordered by date.
E.g.,
Category x
DATE NAME ORIGTABLE
1/2/11 SmithToJones Letters
1/3/11 Filing on X Filings
1/4/11 Note re X Notes
1/7/11 JonesToSmith Letters
I'm just stumped on whether there is a good or straightforward way to do this. Open to options. Thanks
I think I have the answer, though it's not perfect. I do this in the controller (may try it in the model next):
$query='select name,date, "letters" as "origtable" from letters
UNION SELECT name,date, "filings" as "origtable" from filings
UNION SELECT name,date, "notes" as "origtable" from notes
ORDER BY date';
$things=$this->Letter->query($query);
$this->set('items',$things);
This gets me an array I can parse out. If I add id to it, then I can easily create links to the relevant item in the relevant table.

Resources