I'l*_*ack 12 mysql normalization database-design transaction
每两周,系统将为公司生成发票。
公司将在每月 1 日和 16 日收到发票。(它将每 2 周通过 Cron Job 运行一次。它会扫描订单表,然后添加到“发票”表中。还有其他选择吗?)
表中有客户订单列表,orders
还标明了它属于哪个公司(orders.company_id
)
该invoice
表计算orders
表中订单的总成本。
我想弄清楚如何设计合理的发票跟踪。有时公司将不得不向我发送费用或有时我向他们发送费用 ( invoice.amount
)
我需要使用以下内容跟踪发票:
这是我提出的数据库设计:
公司表
mysql> select * from company;
+----+-----------+
| id | name |
+----+-----------+
| 1 | Company A |
| 2 | Company B |
+----+-----------+
Run Code Online (Sandbox Code Playgroud)
Customers can select a company from my website.
orders table
mysql> select * from orders;
+----+---------+------------+------------+---------------------+-----------+
| id | user_id | company_id | total_cost | order_date | status_id |
+----+---------+------------+------------+---------------------+-----------+
| 1 | 5 | 2 | 25.00 | 2012-02-03 23:30:24 | 1 |
| 2 | 7 | 2 | 30.00 | 2012-02-13 18:06:12 | 1 |
+----+---------+------------+------------+---------------------+-----------+
Run Code Online (Sandbox Code Playgroud)
two customers have ordered the products from Company B (orders.company_id = 2
). I know the orders fields is not enough, just simplified for you.
orders_products table
mysql> select * from orders_products;
+----+----------+------------+--------------+-------+
| id | order_id | product_id | product_name | cost |
+----+----------+------------+--------------+-------+
| 1 | 1 | 34 | Chair | 10.00 |
| 2 | 1 | 25 | TV | 10.00 |
| 3 | 1 | 27 | Desk | 2.50 |
| 4 | 1 | 36 | Laptop | 2.50 |
| 5 | 2 | 75 | PHP Book | 25.00 |
| 6 | 2 | 74 | MySQL Book | 5.00 |
+----+----------+------------+--------------+-------+
Run Code Online (Sandbox Code Playgroud)
List of products what customers have ordered.
invoice table
mysql> select * from invoice;
+----+------------+------------+---------------------+--------+-----------+
| id | company_id | invoice_no | invoice_date | amount | status_id |
+----+------------+------------+---------------------+--------+-----------+
| 7 | 2 | 123 | 2012-02-16 23:59:59 | 55.00 | 1 |
+----+------------+------------+---------------------+--------+-----------+
Run Code Online (Sandbox Code Playgroud)
This is where I am quite stuck on invoice tables design. I am not sure how it should be done. Invoices will be generated every 2 weeks. From the result example invoice.amount
is 55.00 because it has been calculated from orders.company_id = 2
table
If the invoice.amount
is -50.00 (minus), it mean company will need to send me the fees amount.
If the invoice.amount
is 50.00, it mean I need to send the company the fees.
The status_id could be: (1)Invoice Sent, (2)Cancelled, (3)Completed
Do I need to add invoice_id
field in the orders
table? Update the orders.invoice_id
field when row has been inserted into 'invoice' table.
invoice_payment table
mysql> select * from invoice_payment;
+----+------------+-----------------+-------------+---------------------+---------------------+
| id | invoice_id | amount_received | amount_sent | date_received | date_sent |
+----+------------+-----------------+-------------+---------------------+---------------------+
| 1 | 1 | 0.00 | 55.00 | 0000-00-00 00:00:00 | 2012-02-18 22:20:53 |
+----+------------+-----------------+-------------+---------------------+---------------------+
Run Code Online (Sandbox Code Playgroud)
This is where I can track and update transaction.. the payment will be made via BACS.
Is this good tables design or what do I need to improve? What fields and tables I should add?
If the invoice has been generated and later I need to make the changes in orders_products
or orders
tables - should it recalculate the invoice.amount
field? (I will be using PHP / MySQL).
SQL Dump:
CREATE TABLE IF NOT EXISTS `company` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(25) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `company` (`id`, `name`) VALUES
(1, 'Company A'),
(2, 'Company B');
CREATE TABLE IF NOT EXISTS `invoice` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`invoice_no` int(11) NOT NULL,
`invoice_date` datetime NOT NULL,
`amount` decimal(6,2) NOT NULL,
`status_id` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;
INSERT INTO `invoice` (`id`, `company_id`, `invoice_no`, `invoice_date`, `amount`, `status_id`) VALUES
(7, 2, 123, '2012-02-16 23:59:59', '55.00', 1);
CREATE TABLE IF NOT EXISTS `invoice_payment` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`invoice_id` int(11) NOT NULL,
`amount_received` decimal(6,2) NOT NULL,
`amount_sent` decimal(6,2) NOT NULL,
`date_received` datetime NOT NULL,
`date_sent` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
INSERT INTO `invoice_payment` (`id`, `invoice_id`, `amount_received`, `amount_sent`, `date_received`, `date_sent`) VALUES
(1, 1, '0.00', '55.00', '0000-00-00 00:00:00', '2012-02-18 22:20:53');
CREATE TABLE IF NOT EXISTS `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`company_id` int(11) NOT NULL,
`total_cost` decimal(6,2) NOT NULL,
`order_date` datetime NOT NULL,
`status_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `orders` (`id`, `user_id`, `company_id`, `total_cost`, `order_date`, `status_id`) VALUES
(1, 5, 2, '25.00', '2012-02-03 23:30:24', 1),
(2, 7, 2, '30.00', '2012-02-13 18:06:12', 1);
CREATE TABLE IF NOT EXISTS `orders_products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`product_name` varchar(100) NOT NULL,
`cost` decimal(6,2) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;
INSERT INTO `orders_products` (`id`, `order_id`, `product_id`, `product_name`, `cost`) VALUES
(1, 1, 34, 'Chair', '10.00'),
(2, 1, 25, 'TV', '10.00'),
(3, 1, 27, 'Desk', '2.50'),
(4, 1, 36, 'Laptop', '2.50'),
(5, 2, 75, 'PHP Book', '25.00'),
(6, 2, 74, 'MySQL Book', '5.00');
Run Code Online (Sandbox Code Playgroud)
Feel free you want to updates/add tables to Answer here.
Thanks
现金匹配
这是一个现金匹配问题。您可以在以下两个级别之一进行跟踪:
将发票与现金数字进行比较(有点草率,但这实际上是大多数劳合社辛迪加对内向业务的处理方式,通常称为“书面与签名”报告)。
从按发票细分的现金支付中维护明确的现金分配。
从你的问题来看,我认为你想做后者。
通常,这是通过拥有一组单独的现金交易和一个将现金支付分配到发票的桥接表来完成的。如果值相等或现金付款带有单个发票参考,您可以自动进行分配。如果发票和付款之间存在 M:M 关系,您将需要执行手动匹配过程(自动执行此操作实际上是背包问题的一个变体)。
一个基本的现金匹配系统
假设您有一个发票表、一个现金支付表和一个分配表。当您开具发票时,您可以在发票表中设置发票记录,并在分配表中设置“应收账款”或“应付账款”记录。
发票 #1,100 美元
分配:参考发票 #1、“应收账款”交易类型和 100 美元欠款的记录。此记录中未提及现金付款。
现在,您将获得 100 美元的现金付款
现金支付 (chq #12345):100 美元
分配:参考发票 #1 和 chq #12345、“现金”交易类型和 -100 欠款(已支付 100 美元)的记录。
您可以将其概括为 M:M 关系,在这种关系中,您可以根据一张发票获得多次付款,或者一笔付款涵盖多张发票。这种结构也使构建信用控制报告变得非常容易。该报告只需要查找超过(例如)180 天但仍有未清余额的发票。
下面是一个模式示例,外加几个场景和一个陈旧的债务查询。不幸的是,我手头没有一个正在运行的 mysql 实例,所以这个实例用于 SQL Server。
-- ==============================================================
-- === CashMatch.sql ============================================
-- ==============================================================
--
-- === Invoices =================================================
--
create table Invoice (
InvoiceID int identity (1,1) not null
,InvoiceRef varchar (20)
,Amount money
,InvoiceDate datetime
)
go
alter table Invoice
add constraint PK_Invoice
primary key nonclustered (InvoiceID)
go
-- === Cash Payments ============================================
--
create table CashPayment (
CashPaymentID int identity (1,1) not null
,CashPaymentRef varchar (20)
,Amount money
,PaidDate datetime
)
go
alter table CashPayment
add constraint PK_CashPayment
primary key nonclustered (CashPaymentID)
go
-- === Allocations ==============================================
--
create table Allocation (
AllocationID int identity (1,1) not null
,CashPaymentID int -- Note that some records are not
,InvoiceID int -- on one side.
,AllocatedAmount money
,AllocationType varchar (20)
,TransactionDate datetime
)
go
alter table Allocation
add constraint PK_Allocation
primary key nonclustered (AllocationID)
go
-- ==============================================================
-- === Scenarios ================================================
-- ==============================================================
--
declare @Invoice1ID int
,@Invoice2ID int
,@PaymentID int
-- === Raise a new invoice ======================================
--
insert Invoice (InvoiceRef, Amount, InvoiceDate)
values ('001', 100, '2012-01-01')
set @Invoice1ID = @@identity
insert Allocation (
InvoiceID
,AllocatedAmount
,TransactionDate
,AllocationType
) values (@Invoice1ID, 100, '2012-01-01', 'receivable')
-- === Receive a payment ========================================
--
insert CashPayment (CashPaymentRef, Amount, PaidDate)
values ('12345', 100, getdate())
set @PaymentID = @@identity
insert Allocation (
InvoiceID
,CashPaymentID
,AllocatedAmount
,TransactionDate
,AllocationType
) values (@Invoice1ID, @PaymentID, -100, getdate(), 'paid')
-- === Raise two invoices =======================================
--
insert Invoice (InvoiceRef, Amount, InvoiceDate)
values ('002', 75, '2012-01-01')
set @Invoice1ID = @@identity
insert Allocation (
InvoiceID
,AllocatedAmount
,TransactionDate
,AllocationType
) values (@Invoice1ID, 75, '2012-01-01', 'receivable')
insert Invoice (InvoiceRef, Amount, InvoiceDate)
values ('003', 75, '2012-01-01')
set @Invoice2ID = @@identity
insert Allocation (
InvoiceID
,AllocatedAmount
,TransactionDate
,AllocationType
) values (@Invoice2ID, 75, '2012-01-01', 'receivable')
-- === Receive a payment ========================================
-- The payment covers one invoice in full and part of the other.
--
insert CashPayment (CashPaymentRef, Amount, PaidDate)
values ('23456', 120, getdate())
set @PaymentID = @@identity
insert Allocation (
InvoiceID
,CashPaymentID
,AllocatedAmount
,TransactionDate
,AllocationType
) values (@Invoice1ID, @PaymentID, -75, getdate(), 'paid')
insert Allocation (
InvoiceID
,CashPaymentID
,AllocatedAmount
,TransactionDate
,AllocationType
) values (@Invoice2ID, @PaymentID, -45, getdate(), 'paid')
-- === Aged debt report ========================================
--
select i.InvoiceRef
,sum (a.AllocatedAmount) as Owing
,datediff (dd, i.InvoiceDate, getdate()) as Age
from Invoice i
join Allocation a
on a.InvoiceID = i.InvoiceID
group by i.InvoiceRef
,datediff (dd, i.InvoiceDate, getdate())
having sum (a.AllocatedAmount) > 0
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
13286 次 |
最近记录: |