Database design: credit top-ups and transactions - mysql

We're in the business of lead generation. Customers receive leads from us. They view basic information about the lead and decide whether to accept the lead or reject. If they accept the lead, they are deducted 1 credit (for each lead) and they can view the detail info of the lead.
So each lead costs 1 credit (which is tied to some monetary value).
We have different type of customers: insurance agents, real estate agents, credit card companies, clubs, etc. They pay different price for 1 credit.
Customers can be prepaid or postpaid.
I have the following tables for all of the above:
Customers (id, name, address, is_postpaid, customer_type_id)
CustomerTypes (id, name, credit_price) // name: insurance agent, real estate agent, etc
TransactionTypes (id, name) // add to or deduct from credit balance
CustomerTransactions (id, customer_id, quantity, transaction_type_id, credit_balance, credit_unit_price, date_created)
Questions:
What do you think about the overall approach?
This design is OK for prepaid customers. But what about postpaid?
Any feedback and criticism is appreciated.
Update:: I've updated above tables to include the FK in Customers table.

If the grain of the transaction table is truly one transaction (no cumulative fields), than there is no difference between pre-paid and post-paid customers. You may enter payments as positive and debits as negative numbers in a same column, or (as some people prefer) use two columns for "positive and negative" customer transactions.
Customer balance can always be obtained by:
select
CustomerName
, sum(Credits) - sum(Debits) as AccountBalance
from Transactions as t
join Customer as c on c.CustomerId = t.CustomerId
group by CustomerName
order by CustomerName ;

I would change the database to the following:
Customers (id, name,customerType_id, address, is_postpaid)
CustomerTypes (id, name, credit_price)
CustomerTransactions (id, customer_id, quantity, transaction_type, credit_balance, credit_unit_price, date_created)
What I am not so sure is about the prepaid or postpaid concept. If you can please clarify it for me.

i would also suggest that there is no difference between pre and post pay - you just have transactions.
to accomodate the pre-post differences, you could introduce a new table that describes it:
customer_type
--------------
id
pre_or_post
begin_date
end_date

One thing about credits is that from an accounting standpoint, they are a liability on the books if prepaid. They are sort of like gift cards, you can't recognize the revenue until the credits are used. With that in mind, you want to have credits expire after a certain length of time, like 1 year. Since credits expire, you then need to deduct credits in order, from oldest to newest.
You should have one table to record credit usage (i.e. CustomerCreditUsage), and another table for credit purchases (CustomerTransactions). Upon purchase, you check for negative credit balance and close out the record(s) from the credit usage table. If prepaid, when you record a credit usage transaction, you deduct from the credit balance of the oldest credit purchase.
Keep in mind that there is a loose relation between credit purchases and credit usage. You can have one without the other. Although you don't want to have a credit usage without a matching credit purchase for too long (post paid).

Related

How to use joins to fetch unique data from a single table in mysql?

I've 4 tables in the database which are:-
branch
deposit
borrow
customers
Query: Find loan no, loan city, deposit account number, deposit city of those customers who are living in Nagpur city?
I've written a query below which successfully fetches three data.
Loan No
Loan City
Deposit Account number
But while fetching Deposit city, I'm getting same data as of Loan city.
Below I'm sharing my code so that you can find where I'm wrong
My tried code:
SELECT borrow.loanno,
branch.city AS 'Borrow city',
deposit.actno,
branch.city AS 'Deposit city',
customer.city As 'Customer_city'
FROM customer
JOIN borrow
ON borrow.cname = customer.cname
JOIN branch
ON borrow.bname = branch.bname
JOIN deposit
ON deposit.cname = customer.cname
AND customer.city = 'Nagpur';
By running the above code, Output I get is
loanno Borrow city Deposit city actno Customer_city
321 Mumbai Mumbai 104 Nagpur
375 Mumbai Mumbai 105 Nagpur
Output I want is:-
loanno Borrow city Deposit city actno Customer_city
321 Mumbai Delhi 104 Nagpur
375 Mumbai Banglore 105 Nagpur
For more understanding I'm sharing my dB fiddle link so that you get a clear idea.
https://www.db-fiddle.com/f/gaUYxuwuJLsWA4kFeMn9u6/6
Please dont take this the wrong way, but what you have does not appear to be a good database design. Typically, each table would have an auto-increment integer-based ID key that is unique and carried through to each underlying table. You have string values for your primary keys.
I know this looks like the beginning of a database design, but you really need some assistance moving forward.
So lets say, you have 5 people named "Anil", what do you do then... your done. "Anil" is the primary key and you cant change it. By having an auto-increment, you could have 1000 Anil customers, each with additional information to ensure uniqueness... first name, last name, address, other unique info.
Now on to your branch lookup table. Same applies. you have multiple branches in the city of Mumbai. WOULD there ever be a duplicate name? Possible??? Hence having an ID column auto-increment would resolve, and additional info like customer would ensure uniqueness like the address. Such as the "Kranti" branch. "IF" you had a Kranti branch on X-street vs Kranti branch on Y-street you would be safe with unique auto-increment numbers.
Now on to deposit and borrow tables. I know its sample data, but just to show how an oops can happen. Your branch info has a name "Chandani", but your deposit table has "Chandni" (missing the second "a"). I know type-o.
When you have your data entry screens and you find a customer or a branch, the system will have the given "id" of the record to store in your given deposit or borrow table. Don't do based on strings per the customer example of Anil -- which one if you have 100 Anil customers. The ID will ensure the correct customer. Similar with branch. The ID columns for customer and branch should be used instead (based on a modified database/table structures).
Finally to your query. You are trying to join the borrow table with the deposit for a given person. This is just going to give you headaches. There is no guarantee of a deposit for a borrow (or vice-versa). Now, if you have 5 borrows and 7 deposits for Anil, and you try to query both the way you have it, you will get 35 records being returned (a Cartesian product) because for every record in the borrow table with matching customer will be applied to those deposits.
So, enough for you to consider, sit back, take a breath and digest it. I can show you a better table structure to handle some context presented here.
As for writing queries, you really need to present it in simple English what you are TRYING to get, then show how you are ATTEMPTING. Also, there appears to be no match such as a deposit being applied to a given borrow, so what's the point / logic on this.
Again, sincere on what you are up against and would like to help but you may be farther off than you realize.
FEEDBACK
Ok, so it is an assignment -- a bad one, but one none-the-less. So the issue is you only used one instance of the branch based on the borrower. What you need is the branch table joined TWICE... once to the borrower, once for the deposit. In each, I "aliased" it. When joining to the borrow table I aliased it as "BBranch" for "borrow branch". For the deposit, I aliased as "DBranch" for "deposit branch". So each one is joined respectively, so each one will point to its own and pull the city respectively.
select
borrow.loanno,
BBranch.city as 'Borrow city',
deposit.actno,
DBranch.city as 'Deposit city',
customer.city
from
customer
join borrow
on customer.cname = borrow.cname
join Branch BBranch
on borrow.bname = BBranch.bname
join deposit
on customer.cname = deposit.cname
and customer.city = 'Nagpur'
join branch DBranch
on deposit.bname = DBranch.bname
Also, indentation to see how / where the tables are joined shows. You can see it goes
customer
borrow
branch for borrow
deposit
branch for deposit
Then you can always see the relationships down stream and not overlapping others / criss-crossing.
The last item is the and customer.city = 'Nagpur'. Don't even know if that was a requirement, but it was in your query.

multiple currencies in a single table

How to make a MySQL table that could contain multiple type of currencies ($, Euros, ...) ? And if I want to make a final report is their a way to make a sum of those currencies other than adding multiple if statements to the sum ?
There may be several aspects involved. So here is some brainstorming:
You offer the same product in different currencies, e.g. a toy for 10 EUR or 11 USD. So you'd probably have an item table plus an item_price table, where the latter has product number, currency and price. (An alternative would be to have just one price, e.g. in USD and a currency table with conversion rates and you'd calculate the foreign price. But then you'd get "ugly" prices, e.g. 10.57, rather than typical prices like 10.50, 10.90 or 10.99. But well, you could have a price adjustment funtion for that, too. And you'd have to keep your conversion tables up-to-date or live with possible losses.)
A customer buys products and pays the bill. The bill is in one currency (store price and currency in the bill), but you also get your money in your currency, as PayPal or the bank convert it at a daily rate. Do you need to store this real amount, too? Then that would be another column in your bill.
I don't know how it is about taxes. Different currencies sounds like different countries. Maybe you'd have to deal with these in your database, too. I just don't know. The prices you show and store are usually gross prices (the price a customer actually pays) and the taxes (could be different VAT percentages with different products in one single bill) would have to be calculated.
As to getting sums: with all the information stored you'd get them with joins from the tables. No if-then-else in my opinion.
As per my opinion you can create a Country Table which contains
CountryID, CountryName, CurrencyCode, ExchangeRate. Now In that
country table you have to add all countries which you want to add but
you have to keep one thing in mind that you have to decide 1 currency
as base currency and put exchangeRate as 1 for that currency and
convert all other currencies exchangeRate based on the base currency
and insert into that table. i.e. Keep base currency as USD so insert
1 record with USD and exchangeRate must be 1 for that currency.
Suppose I am adding India as country than exchangeRate for that
country is based on USD as 66.40 Rs. insert all other countries
according to this entries.
Now when you want to add any transaction related to money in any
table then keep exchangeRate column with that amount column. ind
insert the exchangeRate and amount same as user's currency. i.e. If
my user is in India country, so it's currency is INR. Now that user
wants to add 1000 Rs. then you have to enter that 1000 Rs amount in
transaction table as it is without any conversion with exchange Rate
of 66.40 (fetch from country table). Same way If user currency is USD
and he wants to add a transaction of 100$ than store 100 as Amount
and exchangeRate as 1.
Now when you want to create any reports then just divide exchangeRate
with Amount, so you will get report in your base currency and after
conversion in base currency you can generate each report in any
currency as per your requirement.

without losing old fees payment in MySQL

i am creating a student management system, i have 3 tables, student, fees, student_fee, the fee table contains the amount of fees and the student_fee contains reference to student.studentid and fee.feeid, so that whenever a student paid their fees, the studentid, feeid and paid_date will be insert into the student_fee table. Fees can increase the next year, how can i still keep the old records of fees payment without losing and causing any problem to the account
Instead of doing an entire history table separately, just add a column into the student_fee_paid table for the amount of the payment... so you can still query from it directly for the entire history of a student and it will have the historical values all in one... ex:
Student Fee_ID DatePaid FeeAmount
X 1 1/1/2009 $25
X 1 1/1/2010 $25
X 1 1/1/2011 $30
X 1 1/1/2012 $35
Otherwise it will just grow to a larger task... If you have 30 different fees, and the fee schedule amount changes for some but not all, what then... You can keep your "Fee" table to reflect whatever the "Current" fee amount is, but as soon as its paid, stamp that amount immediately.... if rates change mid-year, yet another trouble / history consideration... don't worry about the otherwise minimal amount of data storage this will increase.
Create a history of fee payment table. StudentID, FeeID, DatePaid, AmountPaid
or Effectively Date your Fee table.
Copy the table fees and student_fee for archive purpose.
You can use the SELECT INTO command.
http://www.w3schools.com/sql/sql_select_into.asp

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.