Client Management DB Design - Track credit based purchases - mysql

My reservation system allows us to purchase credits for clients in terms of pre defined packages. I'm struggling with how I record and calculate available credits.
Let's say we're talking about a car wash service. A client can have multiple cars and can purchase the following services, 'Wash and Wax' and 'Detailing'.
Client 1 has two cars, Car A and Car B. He brings them both in and purchases:
Car A - 1 Wash and Wax
Car A - 1 Detailing
Car B - 10 Wash and Wax
Car B - 1 Detailing
This generates 4 rows in my Purchases table, one for each service purchased.
In my DB I have two related tables tracking purchases and reservations. Table 1 Purchases, Table 2 Reservations.
In Purchases I have the following fields of note:
id
client_id
car_id
service_id
credits_purchased
credits_scheduled
credits_used
cart_id
Then in my Reservation table I have the following fields of note:
id
client_id
car_id
service_id
reservation_date
completed_datetime
car_in_datetime
car_out_datetime
purchase_id
I track the credits available by updating the Purchases table fields credits_used and credits_on_schedule as events happen.
For example, when the client makes a reservation the system adds a new record in the Reservations table, once this happens the system also runs an update query and adds +1 to the related Purchases table credits_on_schedule. When the Reservation is updated to complete the system also updates the Purchases table and adds -1 to credits_on_schedule and +1 to credits used. Simple math between credits_purchased, credits_used, and credits_on_schedule derive what credits are available for a client to use.
I feel like this isn't a good way to track the credits. My question is what is a better implementation? Should I just track credits_purchased then use count queries on the Reservation table to calculate credits_used and credits_on_schedule? Should I be using a pivot table to track? I can't seem to wrap my head around what is the cleanest design.

It looks to me that the design is ok in general.
A reservation can only have one purchased related to it so purchase_id field is a foreign key in Reservation table.
Nevertheless, my advise to you is to create a log system of all these record updates.
As you mentioned, as events are fired the system updates the calculated fields.
What if for some reason the system fails at a certain point? You should be able to track these events.
One way to avoid this is, as you mentioned, calculate credit_used by a count query on all completed reservations.

Related

Create a table using primary key of another table, that is composed of multiple columns

In hotel booking websites, hotelier creates rate plans for each room category (Super Delux, Suit Room, etc.). And each rate plan has rates based on many things.
e.g. followings are considered as rate plans
Super Delux room (Room only) (Payment mode: pay#hotel)
Super Delux room (Room only) (Payment mode: pay now)
Super Delux room (Room + breakfast) (Payment mode: pay#hotel)
Super Delux room (Room + breakfast)(Payment mode: pay now)...
Followings are the things on which a rate plan varies
1: Date - Rates can be different for every day
2: Contract Type - Rates on application booking,website booking and travel agents are different
3: Occupancy Type - Rate for single person, 2 people and 3 people are different
4: rate_plan_rate_master is used to save these rates. We need to keep 2 years of data in this table.
.
.
.
50,000 Hotels * 40 Rate Plans * 6 Occupancy Types * 4 contract types * 730 Days = 8,760,000,000 records
Followings are the different approaches that I could think of
APPROACH 1
35,040,000,000 Records
.
.
.
rate_plan_rate_master table
date (pk)
id_rate_plan (pk/fk)
contract_type (pk/fk)
id_occupancy_type (pk/fk)
rate
.
.
.
APPROACH 2 : Dont keep occupancies in different tables
5,840,000,000‬ Records
rate_plan_rate_master table
date (pk)
id_rate_plan (pk/fk)
contract_type (pk/fk)
rate_single
rate_double
rate_triple
rate_extra_child
rate_extra_adult
rate_extra_infant
.
.
.
APPROACH 3 : Create different tables to save rates for different contract types (split table into tables)
1,168,000,000‬ Records in each table
rate_plan_rate_master_web
rate_plan_rate_master_mobile
rate_plan_rate_master_b2b
rate_plan_rate_master_travel_agent
Following things will help you understand the requirements :
Occupancy types won't change in the near future. Maybe 1 or 2 types could be added as extra_child2, extra_adult2...
Usually not all the hoteliers enter the price for all types of occupancies... Small hotels don't add prices for Triple, Squad, extra_infant, extra_child.
Should I use a composite key? Or the primary key of one column and unique constraint on other fields.
This table's PK will be passed in booking_details table
This table requires only 2 years of data, but that doesn't mean we would remove it once it gets older. We want the data for analytic purposes. So right now what I can think of is that every day we will run a service or job in laravel that will remove 2 years old data from this table and add it somewhere else. If this is a solution please help me where should I send it, another table, CSV file or something else.

ssis duplicate rows and script componenet

So, not sure how to explain this but here goes. I am trying to translate a table of commissions earned. Some of the records have the same invoice and item meaning multiple people earned commissions on that product. I want to merge those records so I only have one record per product per invoice. Basically, like a pyramid scheme, salespeople make commissions on sales and depending on what level they are at, (1,2,3,4) all salespeople above them get a commission as well. So I need to find records where invoice and item are the same,
pull data from each of those records and then output only one record with that data.
I am pretty sure a script component is the way to go but I am not sure how to pull multiple rows of data in to check for duplicate invoice and item, also keeping some of the data for each row to output together.
Let me know if you have any questions, I know I didn't word that the best.
The source table looks like this
COMMISSION Table
SalespersonID - the person getting the commission
InvoiceID - ID of invoice
ItemID - ID of specific lineitem on invoice
ProductID - ID of product sold
Sales_Amt - total for that lineitem
Salesperson Downlevel ID - ID of salesperson below SalespersonID if exists
Commission Rate - percentage Salesperson receives
Commission Total - amt salesperson receives
There are some other columns but these are the important ones.
I am translating this into a table that looks like this
New COMMISSION Table
CommissionId - Primary key for commission table
CommissionPayable - amt salesperson gets
CommissionPayableUpline1 - amt salespersonUpline1 gets
CommissionPayableUpline2 - amt salespersonUpline2 gets
CommissionPayableUpline3 - amt salespersonUpline3 gets
CommissionRate - percent salesperson gets of amount
CommissionRateUpline1 - percent salesperson upline1 gets
CommissionRateUpline2 - percent salesperson upline2 gets
CommissionRateUpline3 - percent salesperson upline3 gets
SalespersonId - Id of salesperson
SalespersonUpline1 - ID of salesperson upline1
SalespersonUpline2 - ID of salesperson upline2
salespersonUpline3 - ID of salesperson upline3
So you see we can have up to four levels of salespeople. Most of our salespeople are only 1 to 2 levels but we do have a few 3 levels deep and none currently that are four levels deep. The way it is stored in the old database is confusing and not very efficient.
I want to find rows with duplicate invoice and item ids, grab the salespeople, and down level salespeople as well as commission rates and amounts for each, then insert all that into one row in new system.
Well I finally got this figured out by using the ssis script component and then doing some fancy sorting and creating new records on output.

How to write the Join Query?

I am having trouble with a join I need to do -
Here's what the problem is:
I have a database of 17 salespeople, each salesperson has his own totals such as expenses and advances and commissions.
In my database there are about 200 customers that can have up to 3 salespeople on them. That is the minority but it does exist. Each salesperson is paid on invoices for that account. Some of my salespeople are only the 2nd salesperson on an account, meaning they are never the 1st, so if I do a join of a salesperson to invoices - he doesn't have any invoices because he is never salesman 1.
I can pull invoices for him as salesman 2 when I am only looking at invoices, but every 8 weeks I need to join his expenses where he is salesman 1 to his invoices where he is salesman 2. I cannot get it to work or come up with a solution.
I have a salesperson query so every 8 weeks I can call salesman number 100 to see his expenses
I have a invoices/payments query and see what he has in for payments
- but my 3rd query where I am trying to bring the salesman together with his payments doesn't work because he is the 1st salesman in expenses and the 2nd salesman in the invoices/payments
I have tried every which way in SQL and can't seem to get it right.
Is there a way I can join the salesman1 from the salesman query to the invoices/payments query on slmn1 or slmn2 or slmn3?
this example is too complex:
FROM qryFinalWeek
INNER JOIN QryFW ON qryFinalWeek.SLMN = QryFW.SLMN2 OR QryFW.SLMN3 OR QryFW.Salesrep1
WHERE (((QryFW.PDATE)=[FDATE]+4));
this example is only giving invoices where he's first salesman so i can't get any commissions if he is the second:
WHERE (((qryFinalWeek.SLMN)=[Forms]![frFWDATE]![Text0]) AND ((QryFW.PDATE)=[FDATE]+4)) OR (((QryFW.SLMN2)=[Forms]![frFWDATE]![Text0]) AND ((QryFW.PDATE)=[FDATE]+4)) OR (((QryFW.SLMN3)=[Forms]![frFWDATE]![Text0]) AND ((QryFW.PDATE)=[FDATE]+4))
any feedback is appreciated!
The data structure has created the problem for you. My suggestion would be to remove the sales people from your Customers table (assuming that there is more data per customer than just the associated sales people) and add them to a new table called Salesmen_Customers or something like that. This table would contain a many to many relationship between Salesmen and Customers
Here is your current structure
And here is the new structure
That should simplify the query structure significantly. This will also scale easily if your sales staff ever goes more that 3 deep. If you are interested is further reading on the topic of database design I'd recommend SQL anti patterns. If you are really married to you DB structure you can query your way out, but I wouldn't recommend it.

Invoice table design

I am creating an invoice table for the customers. Sometime they will have to send me the commission fees or sometime I send them fees (account balance). How to design a invoice table in this situation?
I have came up with this solution, im not sure if this is correct or what is the alternative way?
tbl_invoice
- invoice_id (PK)
- order_id (FK)
- invoice_date
- amount (copy the price from tbl_order.total table)
- status (Invoice Sent, Cancelled, Amount Received, Amount Sent)
tbl_Payments
- invoice_id (FK)
- amount_received (recieved commission fees from customer)
- amount_sent (sent fees to customer)
- date_received
- date_sent
if the tbl_invoice.amount is -30.00 that mean customer will send me the fees.
if the tbl_invoice.amount is 30.00 then I will send the customer the fees.
Do I need tbl_invoice.amount field?
If you could redesign my tables how it should be that be great.
A few things:
Normalize invoice status to be its own lookup table, then put a Status ID in the invoice table rather than 'Sent', 'Cancelled', etc.
Definitely keep invoice amount. This might have to be different from the price value in tbl_order.total if you ever need to take into account discounts. In any case, numerical data is cheap to store and will be faster to query if you dont have to do any joins.
Give the Payments table its own ID column and make it the PK.
The rest looks ok. There is a case for having two tables, one for payments outgoing, and another for payments incoming. If you really only need to keep the amount and date information, then I dont think you need to make it any more complicated.
Thanks,
Chris.
You should be tracking:
amount_due
amount_sent
amount_received
All three are important. And in the payments, also track the fee_paid.
Lastly, it's usually better to use T-ledger accounting (i.e. debit/credit) for this kind of stuff. It makes things much cleaner if you ever need to worry about discounts, coupons, refunds, chargebacks, etc.

Invoice table: Send fees and Receive Fees [duplicate]

I am creating an invoice table for the customers. Sometime they will have to send me the commission fees or sometime I send them fees (account balance). How to design a invoice table in this situation?
I have came up with this solution, im not sure if this is correct or what is the alternative way?
tbl_invoice
- invoice_id (PK)
- order_id (FK)
- invoice_date
- amount (copy the price from tbl_order.total table)
- status (Invoice Sent, Cancelled, Amount Received, Amount Sent)
tbl_Payments
- invoice_id (FK)
- amount_received (recieved commission fees from customer)
- amount_sent (sent fees to customer)
- date_received
- date_sent
if the tbl_invoice.amount is -30.00 that mean customer will send me the fees.
if the tbl_invoice.amount is 30.00 then I will send the customer the fees.
Do I need tbl_invoice.amount field?
If you could redesign my tables how it should be that be great.
A few things:
Normalize invoice status to be its own lookup table, then put a Status ID in the invoice table rather than 'Sent', 'Cancelled', etc.
Definitely keep invoice amount. This might have to be different from the price value in tbl_order.total if you ever need to take into account discounts. In any case, numerical data is cheap to store and will be faster to query if you dont have to do any joins.
Give the Payments table its own ID column and make it the PK.
The rest looks ok. There is a case for having two tables, one for payments outgoing, and another for payments incoming. If you really only need to keep the amount and date information, then I dont think you need to make it any more complicated.
Thanks,
Chris.
You should be tracking:
amount_due
amount_sent
amount_received
All three are important. And in the payments, also track the fee_paid.
Lastly, it's usually better to use T-ledger accounting (i.e. debit/credit) for this kind of stuff. It makes things much cleaner if you ever need to worry about discounts, coupons, refunds, chargebacks, etc.