生成发票和跟踪


11

系统每2周就会为公司生成发票。

公司将在每月的1号和16号收到发票。(它将每2周通过Cron Job运行一次。它会扫描订单表,然后将其添加到“发票”表中。是否有其他选择?)

表格中有客户订单的清单,orders还指出了它属于的公司(orders.company_id

invoice表从orders表中计算订单的总成本。

我试图弄清楚如何设计合理的发票跟踪。有时公司会向我发送费用,或者有时我会向他们发送费用(invoice.amount

我需要使用以下方式跟踪发票:

  • 公司给我汇款时
  • 我什么时候汇款到公司的
  • 从公司收到了多少钱
  • 我寄给公司多少钱
  • 我收到了全部款项吗(如果没有,我需要在Db上更新什么?)
  • 发票状态(已发送,已取消,已收金额,已发送金额)

这是我想出的数据库设计:

公司表

mysql> select * from company;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | Company A |
|  2 | Company B |
+----+-----------+

客户可以从我的网站选择一家公司。

订单表

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 |
+----+---------+------------+------------+---------------------+-----------+

两名客户从B公司orders.company_id = 2)订购了产品。我知道订单字段还不够,只是为您简化了订单。

orders_products表

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 |
+----+----------+------------+--------------+-------+

客户订购的产品清单。

发票表

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 |
+----+------------+------------+---------------------+--------+-----------+

这就是我在发票表设计上相当棘手的地方。我不确定应该怎么做。发票将每2周产生一次。结果示例invoice.amount为55.00,因为它是根据orders.company_id = 2表计算得出的

如果invoice.amount-50.00(减),则表示公司需要向我发送费用金额。

如果invoice.amount是50.00,则表示我需要向公司发送费用。

status_id可以是:(1)已发送发票,(2)已取消,(3)已完成

我需要invoice_idorders表格中添加字段吗?将orders.invoice_id行插入“发票”表后,更新字段。

发票付款表

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 |
+----+------------+-----------------+-------------+---------------------+---------------------+

这是我可以跟踪和更新交易的地方。付款将通过BACS进行。

这是好的表设计还是我需要改进的地方?我应该添加哪些字段和表格?

如果已生成发票,以后需要在orders_productsorders表中进行更改-是否应重新计算该invoice.amount字段?(我将使用PHP / MySQL)。

SQL转储

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');

随时您要更新/添加表格以在此处回答。

谢谢

Answers:


8

现金配套

这是一个现金匹配问题。您可以在以下两个级别之一进行跟踪:

  • 将发票与现金数字进行比较(有些草率,但这实际上是大多数劳埃德集团辛迪加进行内向业务的方式,通常称为“书面与签名”报告)。

  • 保留按发票细分的现金付款中的明确现金分配。

从您的问题来看,我认为您想这样做。

通常,这是通过具有一组单独的现金交易和具有将现金付款分配给发票的桥接表来完成的。如果值相等或现金付款带有单个发票参考,则可以自动进行分配。如果发票和付款之间存在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

我的有单独的发票和付款表。您可以使用具有内部链接的公用表。现金匹配通常在会计系统中以这种方式实现。
ConcernedOfTunbridgeWells

我设法将您的SQL Server示例转换为MySQL。我经历了,现在我已经很了解了。会什么AllocationType会,如果我想送客户的钱?我是否也需要插入CashPayment表格中(比如说通过BACS付款)?
我将

1
是的,您需要收款和付款的现金付款记录。现金匹配交易的实际交易类型取决于您。
ConcernedOfTunbridgeWells

1
如果需要,您可以将发票上的交易双向匹配一次结算付款。例如:100美元的去往发票,50美元的去往发票(-50)和50美元的收款余额与这两个发票相匹配。
ConcernedOfTunbridgeWells
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.