會計學中的靜態和動態
財務程序員和用戶通常發現,電子表格格式對基本會計數據的某些方面很適合;修改立即生效,並且可以研究試算場景而無需改變基本的會計記錄。
然而這也有點小問題,靜態報表,例如收入和支出、試算表(trial balance)和負債表(balance sheets),可能是由不同的應用程序生成的,需要訪問動態數據來完成報表。從一個應用程序向另一個復制和粘貼不但耗時,而且會導致錯誤,使用逗號隔開值格式和其他技術轉移信息又比較笨拙。
電子表格以 XML 格式存儲數據,支持 XML 的靜態報表程序可以直接讀取數據。該示例使用 Gnumeric 作為報表應用程序,Gnumeric 以 XML 格式存儲數據,就像電子表格和 PHP 一樣,可以直接讀取 XML。
示例:折舊減值准備
一個典型的例子是折舊或資本成本減值准備(capital cost allowances,CCA)。如果生產流程中使用的一台機器的壽命有很多年,那麼它的價值將隨著時間消耗,並需要在使用年限到期時更換。會計應確定每年折舊額的百分比。這個百分比不要求每年相等,稅務機關可能允許在任一年采用某個最大百分比,但由企業確定當前報告期間什麼樣的百分比適合企業,但這個百分比要小於或等於稅務機關允許的最大值。
一個動態電子表格可以顯示幾年的記錄,其中有以前幾年的折舊額歷史,也有以後幾年的折舊額。解決程序(Solver)和其他統計工具幫助企業所有者確定今年采用的折舊額。一旦決定,該值必須顯示在靜態報表應用程序中,這有多種方法,效率有高有低。
圖 1 展示了一個簡單示例2005 年 — 2014 年折舊或 CCA 電子表格屏幕抓圖 。
圖 1. 折舊或 CCA 電子表格樣例
2005 年初購買的一台機器,價格為 $30,000。稅務機關允許此類機器年折舊率最大為 20%。在前兩年,企業決定使用 10% 的折舊率,2010 年,企業決定用 15% 的折舊率。在電子表格(單元格 C8)中修改的值會立即更新到表格中以後幾年的條目中。
原始電子表格數據
由於壓縮的 Gnumeric 文件是純 XML 文件,用一個基本的文本編輯器就可以對數據的結構有一個大概的了解。注意,Gnumeric 可以以壓縮格式存儲電子表格數據。要想直接查看數據,確保在 Gnumeric 參數設置中選擇零壓縮來對電子表格進行存儲。
查看整個 Gnumeric 文件 XML 結構的一個較好的方法是將其以 .xml 擴展名復制到一個新文件中,然後用一個支持 XML 的浏覽器打開。這使您可以根據需要折疊或展開元素。
很多數據盡管在電子表格環境中非常重要,但是對於簡單的數據交換來說則是多余的,因此您需要關注真實的數據。
清單 1 是選自 Gnumeric 保存的數據表。
清單 1. 一節數據文件摘錄
<?XML version="1.0" encoding="UTF-8"?>
<gnm:Workbook XMLns:gnm="http://www.gnumeric.org/v10.dtd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.gnumeric.org/v9.xsd">
<gnm:Version Epoch="1" Major="9" Minor="13" Full="1.9.13"/>
<gnm:Attributes ...>
...
<gnm:Sheets>
...
<gnm:Sheet ... >
...
<gnm:Name>Sheet1</gnm:Name>
...
<gnm:Cells>
<gnm:Cell Row="0" Col="0" ValueType="60">A depreciation example</gnm:Cell>
...
根元素是 <gnm:Workbook ... >,其中 gnm 是前綴,表示名稱空間。Workbook 元素有許多子元素,其中之一是 Sheets。Sheets 元素又有其他子元素,Sheet 元素是其中之一,每一個子元素將數據包含在 Cell 元素中,Cell 元素是 Cells 傘狀元素的一個子元素。因此,為了獲取數據,必須深入 4 層。在這個清單中,您將看到在 0 行 0 列的單元格中有一個字符串 A depreciation example。0 行 0 列對應於電子表格中的 A1 單元格。還要注意,數據表的名稱 Sheet1 存儲在其自己的子元素中,該子元素的父元素是 Sheet。
用 PHP 提取數據
現在您已知道數據存儲在文檔中的什麼地方,用 PHP 例程提取數據應該比較簡單了。然而,您很快會發現有兩個問題讓提取操作不是那麼簡單:
名稱空間
計算的單元格內容
名稱空間
名稱空間做了很多工作來確保 XML 符合預期的模式,但也增加了信息檢索的復雜性。如果不使用名稱空間,PHP 中的 SimpleXML 函數將會失效。調用一個諸如 children() 的方法需要傳遞描述名稱空間前綴和指明其是否被用作前綴的參數。此外,帶有名稱空間前綴的元素的相關屬性值的檢索略有不同。
計算的單元格內容
電子表格中的單元格可以包含固定值或計算的表達式,如果讓靜態報表引擎尋找計算表達式的內容,將會導致使用與電子表格相同的方式重新計算此值。因此,事實上,如果可以只引用包含固定值的單元格,可以更省事。
一個示例 PHP 函數
清單 2 是一個示例 PHP 函數。
清單 2. 提取數據
function read_cca_data() {
$ccafile = "sscca.gnumeric";
if (!file_exists($ccafile)) {
dIE("Problem: CCA file $ccafile not found");
} else {
$xml = simpleXML_load_file($ccafile);
foreach ($XML->children('gnm',TRUE) as $child1) {
if ($child1->getName() == "Sheets") {
foreach ($child1->children('gnm',TRUE) as $child2) {
foreach ($child2->children('gnm',TRUE) as $child3) {
if ($child3->getName() == "Name") {
if ($child3 != "Sheet1") break 2;
}
if ($child3->getName() == "Cells") {
foreach ($child3->children('gnm',TRUE) as $child4) {
// child4 is a cell
$row = (int) $child4->attributes()->Row;
$col = (int) $child4->attributes()->Col;
$val = "$child4"; // quotes evaluate to the type, string or numeric
$val = (is_numeric($val)) ? round($val,3) : $val;
$ccaarr[$row][$col] = $val;
}
}
}
}
}
}
}
return $ccaarr;
}
這段代碼以定義一個 Gnumeric 文件名開始,接著是一個 trap,用於在不找到文件時退出 PHP。如果文件存在,就加載到 SimpleXML 對象中,並深入 4 層($child1,$child2,$child3,$child4),在單個單元格中尋找數據。在每一級,通過驗證元素名或工作表名稱,檢查它是否具有正確的子元素。每次調用 children() 方法將指定名稱空間以及它被用作前綴。在單元格層,它得到存儲在 Row 和 Col 屬性(例如,$child4->attributes()->Row)中的值(行號或列號),並將此值轉換為整數,存儲起來以備以後使用。如果在單元格中找到的值是數值,就保留三位小數。然後,值(字符串或數值)存儲到一個二維數組中,讀取完整個工作表之後,該數組被返回給調用例程。調用程序使用已知下標從數組中選擇所需的值。
Gnumeric 和 PHP 共同合作
回到折舊示例,圖 2 是 圖 1 的一個修改。
圖 2. 另一個折舊或 CCA 電子表格樣例
查看原圖(大圖)
新單元格區域 F3:H12 是單元格 B3:D12 的一個復制,選擇性粘貼的。因為原始單元格區域中的某些值是需要計算的,那麼新單元格區域則是有用的,因為其中的值是固定的,因而是直接可讀的。在電子表格中很容易自動化復制-粘貼操作。或者,考慮到數組中的原始數據是直接可用的,您可以對所需要的值進行重新計算。在第 2 行中,數字 5、6 和 7 僅僅是一個列號提醒,列號從零開始,列 A 是 0 列。列 E 是一個空白列 — 必須記住在 XML 文件中它仍然被作為單元格記錄,只是沒有值。
從報表應用程序調用 清單 2 中的函數會創建一個多維數組 — 一些樣例值是 $ccaarr[2][0]=2005 和 $ccaarr[2][6]=0.1。
結束語
正如您所看到的,Gnumeric 和 PHP 的結合兼具了兩者的長處。Gnumeric 電子表格適應多變的信息並以 XML 格式存儲數據。PHP 報表應用程序可以直接讀取 XML,而不會影響數據的精度。
testrun.zip 文件中含有本文使用的 Gnumeric 文件以及 清單 2 中描述的 PHP 函數的基本實現。
本文示例源代碼或素材下載