2週間ごとに、システムは会社の請求書を生成します。
会社は毎月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 |
+----+-----------+
顧客は私のウェブサイトから会社を選択できます。
注文表
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 |
+----+---------+------------+------------+---------------------+-----------+
2人の顧客がB社(orders.company_id = 2
)に製品を注文しました。 Ordersフィールドは十分ではなく、単純化されているだけです。
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
はorders.company_id = 2
テーブルから計算されているため、55.00になります。
invoice.amount
が-50.00(マイナス)の場合、会社が手数料の金額を送金する必要があることを意味します。
invoice.amount
が50.00の場合、会社に手数料を送る必要があることを意味します。
Status_idは次のようになります:(1)送信済み請求書、(2)キャンセル済み、(3)完了済み
orders
テーブルにinvoice_id
フィールドを追加する必要がありますか?行が「invoice」テーブルに挿入されたら、orders.invoice_id
フィールドを更新します。
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 |
+----+------------+-----------------+-------------+---------------------+---------------------+
ここでトランザクションを追跡および更新できます。支払いはBACS経由で行われます。
これは良いテーブルデザインですか、それとも改善する必要があるのですか?追加する必要があるフィールドとテーブルは?
請求書が生成され、後でorders_products
またはorders
テーブルに変更を加える必要がある場合、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');
ここで回答するためにテーブルを更新/追加することをご自由にどうぞ.
ありがとう
現金照合
これは現金照合問題です。これは、次の2つのレベルのいずれかで追跡できます。
請求額と現金の数値を比較します(多少ずさんですが、実際には、多くのロイドシンジケートが対内取引でこれを行う方法であり、しばしば「書面対署名済み」レポートと呼ばれます)。
請求書で分類された現金支払いからの明示的な現金配分を維持します。
あなたの質問から私はあなたが後者をしたいと思います。
通常、これは、個別の一連の現金トランザクションと、請求書への現金支払いの割り当てを持つブリッジテーブルを使用して行われます。値が等しい場合、または現金支払いに単一の請求書参照が付属している場合は、割り当てを自動的に行うことができます。請求書と支払いの間に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