PageRenderTime 58ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/A2Billing_UI/lib/A2B_invoice.php

https://github.com/xrg/a2billing
PHP | 817 lines | 513 code | 164 blank | 140 comment | 137 complexity | f1d0edd8fc1da1e5f148da2b4775f878 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. // To sort charge type in correct category
  3. $chargetype_list = array();
  4. $chargetype_list[1] = gettext("Connection charge for DID setup");
  5. $chargetype_list[2] = gettext("Monthly Charge for DID use");
  6. $chargetype_list[3] = gettext("Subscription fee");
  7. $chargetype_list[4] = gettext("Extra charge");
  8. $chargetype_list[5] = gettext("Product Purchase");
  9. $chargetype_list[6] = gettext("Refund");
  10. Class A2B_Invoice {
  11. ////
  12. // System
  13. ////
  14. var $verbose_level;
  15. var $instance_table;
  16. var $DB_Handle;
  17. var $base_currency;
  18. var $currencies_list;
  19. ////
  20. // Initial Information for generation
  21. //////
  22. // Additional filters for Invoice menu
  23. // filter_***_op:
  24. // 1: is
  25. // 2: begins with
  26. // 3: contains
  27. // 4: ends with
  28. // 5: different from
  29. var $filter_provider;
  30. var $filter_trunk;
  31. var $filter_destination;
  32. var $filter_destination_op;
  33. var $filter_source;
  34. var $filter_source_op;
  35. ////
  36. // Collected information
  37. //////
  38. // Will be computed according to previous invoices
  39. var $cover_call_startdate;
  40. var $cover_call_enddate;
  41. var $cover_charge_startdate;
  42. var $cover_charge_enddate;
  43. var $previous_balance;
  44. // Information about invoiced customer
  45. var $customer_cardid;
  46. var $customer_username;
  47. var $customer_creation_date;
  48. var $customer_VAT;
  49. var $customer_creationdate;
  50. var $customer_lastname;
  51. var $customer_firstname;
  52. var $customer_address;
  53. var $customer_city;
  54. var $customer_state;
  55. var $customer_country;
  56. var $customer_zipcode;
  57. var $customer_phone;
  58. var $customer_email;
  59. var $customer_fax;
  60. var $customer_invoicetemplate;
  61. var $customer_outstandingtemplate;
  62. var $customer_currentbalance;
  63. var $customer_currency;
  64. // List of Calls Grouped by destination
  65. // [n][0]: destination
  66. // [n][1]: totaltime
  67. // [n][2]: totalsellcost
  68. // [n][3]: nbcall
  69. // [n][4]: totalbuycost
  70. var $list_total_destination;
  71. // List of Charges
  72. // [n][0]: id
  73. // [n][1]: id_cc_card
  74. // [n][2]: iduser
  75. // [n][3]: creationdate
  76. // [n][4]: amount
  77. // [n][5]: currency
  78. // [n][6]: chargetype
  79. // [n][7]: description
  80. // [n][8]: id_cc_did
  81. // [n][9]: cover_to
  82. // [n][10]: cover_from
  83. // [n][11]: id_cc_card_subscription
  84. // [n][12]: cc_card_subscription -> product name
  85. // [n][13]: cc_subscription_fee -> label
  86. var $list_total_charge;
  87. ////
  88. // Generated Invoice
  89. //////
  90. // Total
  91. var $invoice_currency;
  92. var $invoice_subtotal;
  93. var $invoice_tax;
  94. var $invoice_total;
  95. // List of invoice items
  96. // [n][0]: invoicesection
  97. // [n][1]: designation
  98. // [n][2]: sub_designation
  99. // [n][3]: start_date
  100. // [n][4]: end_date
  101. // [n][5]: bill_date
  102. // [n][6]: calltime
  103. // [n][7]: nbcalls
  104. // [n][8]: quantity
  105. // [n][9]: buy_price
  106. // [n][10]: price
  107. var $list_items;
  108. var $list_category_items; // List items, once sorted. 3 dims array
  109. ////
  110. // Billed Invoice (official)
  111. //////
  112. // Storage
  113. var $billedinvoice_id;
  114. var $billedinvoice_default_template;
  115. var $billedinvoice_filename; // PDF file
  116. var $billedinvoice_creationdate;
  117. // Payment status:
  118. // 0: UNPAID
  119. // 1: SENT-UNPAID
  120. // 2: SENT-PAID
  121. // 3: PAID
  122. var $billedinvoice_paymentstatus;
  123. ////
  124. //// Initialisation & Global Methods
  125. ////
  126. function __construct($DB_Handle, $verbose_level=0) {
  127. $this->DB_Handle = $DB_Handle;
  128. $this->instance_table = new Table();
  129. $this->verbose_level = $verbose_level;
  130. $this->currencies_list = get_currencies($this->DB_Handle);
  131. global $A2B;
  132. $this->base_currency = $A2B->config['global']['base_currency'];
  133. }
  134. function RetrieveInformation($customer_cardid, $billcalls, $billcharges, $enddate) {
  135. $this->ReadCardInfo($customer_cardid);
  136. $this->FindCoverDates($billcalls, $billcharges, $enddate);
  137. $this->ListCalls();
  138. $this->ListCharges();
  139. }
  140. ////
  141. //// Gathering Information
  142. ////
  143. // Look for older invoices to start from
  144. // base on customer_cardid
  145. // generate cover_(call/charge)_(start/end)date
  146. function FindCoverDates($billcalls, $billcharges, $enddate) {
  147. if (! isset($this->customer_cardid))
  148. throw new Exception('Unable to find cover dates since customer_cardid not set');
  149. // Here we have to check for the Last Invoice date to set the Cover Start date.
  150. // if a user dont have a Last invocie then we have to Set the Cover Start date to it Creation Date.
  151. $query_billdate = "SELECT CASE WHEN max(cover_call_enddate) is NULL THEN '0001-01-01 00:01:00' ELSE max(cover_call_enddate) END, ".
  152. "CASE WHEN max(cover_charge_enddate) is NULL THEN '0001-01-01 00:01:00' ELSE max(cover_charge_enddate) END ".
  153. "FROM cc_invoice WHERE cardid='$this->customer_cardid'";
  154. if ($this->verbose_level>=1) echo "\nQUERY_BILLDATE = $query_billdate";
  155. $resdate = $this->instance_table -> SQLExec ($this->DB_Handle, $query_billdate);
  156. if ($this->verbose_level >= 2) print_r($resdate);
  157. if (!is_array($resdate))
  158. throw new Exception('Unable to find cover dates');
  159. // Call Dates
  160. $this->cover_call_startdate = ($resdate[0][0] != "0001-01-01 00:01:00") ?
  161. $resdate[0][0] // Customer Last Invoice Date
  162. : $this->customer_creationdate; // Customer Creation Date
  163. $this->cover_call_enddate = ($billcalls == "Yes") ?
  164. $enddate
  165. : $this->cover_call_startdate;
  166. // Charge Dates
  167. $this->cover_charge_startdate = ($resdate[0][1] != "0001-01-01 00:01:00") ?
  168. $resdate[0][1] // Customer Last Invoice Date
  169. : $this->customer_creationdate; // Customer Creation Date
  170. $this->cover_charge_enddate = ($billcharges == "Yes") ?
  171. $enddate
  172. : $this->cover_charge_startdate;
  173. // Display Result
  174. if($this->verbose_level >= 1) {
  175. echo "\n Cover Calls for '$this->customer_username', Start Date:".$this->cover_call_startdate.', End Date:'.$this->cover_call_enddate;
  176. echo "\n Cover Charges for '$this->customer_username', Start Date:".$this->cover_charge_startdate.', End Date:'.$this->cover_charge_enddate;
  177. }
  178. // If Last Invoice for Call and Dates is the same, then we can get balance of previous invoice
  179. if (($this->cover_call_startdate == $this->cover_charge_startdate) && ($this->cover_call_startdate != $this->customer_creationdate)) {
  180. $query_lastbalance = "SELECT current_balance".
  181. " FROM cc_invoice".
  182. " WHERE cardid='$this->customer_cardid'".
  183. " ORDER BY cover_call_enddate DESC".
  184. " LIMIT 1";
  185. if ($this->verbose_level>=1) echo "\nQUERY_LASTBALANCE = $query_lastbalance";
  186. $reslastbalance = $this->instance_table -> SQLExec ($this->DB_Handle, $query_lastbalance);
  187. if ($this->verbose_level >= 2) print_r($reslastbalance);
  188. if (is_array($reslastbalance) && count($reslastbalance) == 1)
  189. $previous_balance = $reslastbalance[0][0];
  190. else
  191. throw new Exception('Unable to find Previous Invoice');
  192. }
  193. }
  194. // List calls according to:
  195. // * filters
  196. // * billing period
  197. // * customer
  198. // Group by destination
  199. function ListCalls() {
  200. if (! $this->IsCoveringCalls()) return;
  201. $Query_Destinations = "SELECT ca.destination, sum(ca.sessiontime), sum(ca.sessionbill), count(*), sum(ca.buycost) FROM cc_call ca";
  202. if (isset($this->filter_provider))
  203. $Query_Destinations .= " JOIN cc_trunk tr USING(id_trunk)";
  204. $Query_Destinations .= " WHERE ca.starttime >= '$this->cover_call_startdate' AND ca.starttime < '$this->cover_call_enddate'";
  205. if (isset($this->customer_username))
  206. $Query_Destinations .= " AND ca.username='$this->customer_username'";
  207. if (isset($this->filter_trunk))
  208. $Query_Destinations .= " AND ca.id_trunk='$this->filter_trunk'";
  209. if (isset($this->filter_provider))
  210. $Query_Destinations .= "$Query_Destinations .= tr.id_provider='$this->filter_provider'";
  211. if (isset($this->filter_destination))
  212. switch ($this->filter_destination_op) {
  213. case 1: $Query_Destinations .= " AND ca.calledstation = '". $this->filter_destination. "'"; break;
  214. case 2: $Query_Destinations .= " AND ca.calledstation LIKE '". $this->filter_destination."%'"; break;
  215. case 4: $Query_Destinations .= " AND ca.calledstation LIKE '%".$this->filter_destination. "'"; break;
  216. case 5: $Query_Destinations .= " AND ca.calledstation <> '". $this->filter_destination. "'"; break;
  217. default:
  218. case 3: $Query_Destinations .= " AND ca.calledstation LIKE '%".$this->filter_destination."%'"; break;
  219. }
  220. if (isset($this->filter_source))
  221. switch ($this->filter_source_op) {
  222. case 1: $Query_Destinations .= " AND ca.src = '". $this->filter_source. "'"; break;
  223. case 2: $Query_Destinations .= " AND ca.src LIKE '". $this->filter_source."%'"; break;
  224. case 4: $Query_Destinations .= " AND ca.src LIKE '%".$this->filter_source. "'"; break;
  225. case 5: $Query_Destinations .= " AND ca.src <> '". $this->filter_source. "'"; break;
  226. default:
  227. case 3: $Query_Destinations .= " AND ca.src LIKE '%".$this->filter_source."%'"; break;
  228. }
  229. $Query_Destinations .= " GROUP BY destination";
  230. if($this->verbose_level >= 1) echo "\n Query_Destinations = $Query_Destinations";
  231. $this->list_total_destination = $this->instance_table -> SQLExec ($this->DB_Handle, $Query_Destinations);
  232. if (!is_array($this->list_total_destination))
  233. throw new Exception('Unable to list calls');
  234. if ($this->verbose_level >= 2) print_r($this->list_total_destination);
  235. if ($this->verbose_level >= 1) echo "\n Number of Destinations for '$this->customer_username' Found: ".count($this->list_total_destination);
  236. }
  237. // List Charges
  238. function ListCharges() {
  239. if (! $this->IsCoveringCharges()) return;
  240. $QUERY_CHARGE = "SELECT cc.id, cc.id_cc_card, cc.iduser, cc.creationdate, cc.amount, cc.currency, cc.chargetype, cc.description, ".
  241. " cc.id_cc_did, cc.cover_to, cc.cover_from, cc.id_cc_card_subscription, cs.product_name, cf.label".
  242. " FROM cc_charge AS cc".
  243. " LEFT OUTER JOIN cc_card_subscription AS cs ON cs.id = cc.id_cc_card_subscription".
  244. " LEFT OUTER JOIN cc_subscription_fee AS cf ON cf.id = cs.id_subscription_fee".
  245. " WHERE creationdate >= '$this->cover_charge_startdate' AND creationdate < '$this->cover_charge_enddate'";
  246. if (isset($this->customer_cardid))
  247. $QUERY_CHARGE .= " AND cc.id_cc_card='$this->customer_cardid'";
  248. if ($this->verbose_level >= 1) echo "\n QUERY_CHARGE = $QUERY_CHARGE";
  249. $this->list_total_charge = $this->instance_table -> SQLExec ($this->DB_Handle, $QUERY_CHARGE, 1);
  250. if (! is_array($this->list_total_charge))
  251. throw new Exception('Unable to list charges');
  252. if ($this->verbose_level >= 2) print_r($this->list_total_charge);
  253. if ($this->verbose_level >= 1) echo "\n Number of Charge for '$this->customer_username' Found: ".count($this->list_total_charge);
  254. }
  255. // Retrieve customer information from database,
  256. // with $customer_cardid, if doesn't exist
  257. // with $customer_username, if doesn't exist, exit
  258. function ReadCardInfo($customer_cardid, $customer_username) {
  259. $QUERY = "SELECT creationdate, lastname, firstname, address, city, state, co.countryname, zipcode, phone, email, fax, vat, username, ca.id, template_invoice, template_outstanding, credit, currency".
  260. " FROM cc_card AS ca JOIN cc_country AS co ON ca.country = co.countrycode ";
  261. // If this invoice is not for only one customer
  262. if (isset($customer_cardid) && $customer_cardid != '')
  263. $QUERY .= "WHERE ca.id = '$customer_cardid'";
  264. elseif (isset($customer_username) && $customer_username != '')
  265. $QUERY .= "WHERE ca.username = '$customer_username'";
  266. else
  267. throw new Exception('Unable to retrieve information on customer since his id is unknown');
  268. if ($this->verbose_level >= 1) echo "\nQUERY_CARD = $QUERY";
  269. $rescard = $this->instance_table->SQLExec($this->DB_Handle, $QUERY);
  270. if (!is_array($rescard) || count($rescard) != 1)
  271. throw new Exception("Cannot find card with id=".$customer_cardid);
  272. if ($this->verbose_level >= 2) print_r($rescard);
  273. $this->customer_creationdate = $rescard[0][0];
  274. $this->customer_lastname = $rescard[0][1];
  275. $this->customer_firstname = $rescard[0][2];
  276. $this->customer_address = $rescard[0][3];
  277. $this->customer_city = $rescard[0][4];
  278. $this->customer_state = $rescard[0][5];
  279. $this->customer_country = $rescard[0][6];
  280. $this->customer_zipcode = $rescard[0][7];
  281. $this->customer_phone = $rescard[0][8];
  282. $this->customer_email = $rescard[0][9];
  283. $this->customer_fax = $rescard[0][10];
  284. $this->customer_VAT = $rescard[0][11];
  285. $this->customer_username = $rescard[0][12];
  286. $this->customer_cardid = $rescard[0][13];
  287. $this->customer_invoicetemplate = $rescard[0][14];
  288. $this->customer_outstandingtemplate = $rescard[0][15];
  289. $this->customer_currentbalance = $rescard[0][16];
  290. $this->customer_currency = $rescard[0][17];
  291. }
  292. ////
  293. //// Piece of information about gathered information
  294. //// All of them return booleans
  295. ////
  296. // Means that it can be billed
  297. function IsOfficial() {
  298. return ( !(isset($this->filter_provider) || isset($this->filter_trunk) || isset($this->filter_destination) || isset($this->filter_source))
  299. && isset($this->customer_username)
  300. );
  301. }
  302. // Calls are covered
  303. function IsCoveringCalls() {
  304. return (isset($this->cover_call_startdate) && isset($this->cover_call_enddate) && ($this->cover_call_startdate != $this->cover_call_enddate));
  305. }
  306. // Charges are covered
  307. function IsCoveringCharges() {
  308. return (isset($this->cover_charge_startdate) && isset($this->cover_charge_enddate) && ($this->cover_charge_startdate != $this->cover_charge_enddate));
  309. }
  310. // Is it a billed invoice ?
  311. function IsBilled() {
  312. return (isset($this->billedinvoice_id));
  313. }
  314. ////
  315. //// Generate Invoice
  316. ////
  317. // Create an invoice from current information:
  318. // * card
  319. // * list of calls
  320. // * list of charges
  321. // * invoice_currency
  322. function CreateInvoice($invoice_currency = '') {
  323. $this->ResetInvoice($invoice_currency);
  324. $this->GenerateCallItems();
  325. $this->GenerateChargeItems();
  326. $this->ComputeTotal();
  327. $this->SortItems();
  328. }
  329. // Prepare the invoice
  330. function ResetInvoice($invoice_currency) {
  331. if ($invoice_currency != '') {
  332. $this->invoice_currency = $invoice_currency;
  333. if ($this->verbose_level >=1) echo "\n Using custom currency";
  334. } elseif (isset($this->customer_currency) && $this->customer_currency!='') {
  335. $this->invoice_currency = $this->customer_currency;
  336. if ($this->verbose_level >=1) echo "\n Using customer currency";
  337. } else {
  338. global $A2B;
  339. $this->invoice_currency = $A2B->config['global']['base_currency'];
  340. if ($this->verbose_level >=1) echo "\n Using base currency";
  341. }
  342. if ($this->verbose_level >=1) echo "\nInvoice Currency is : $this->invoice_currency";
  343. $this->list_items = Array();
  344. }
  345. // Add Call Items
  346. function GenerateCallItems() {
  347. if (! $this->IsCoveringCalls()) {
  348. if ($this->verbose_level >= 1) echo "\nCalls not covered. Skipping CallItems generation.";
  349. return;
  350. }
  351. $nb_items = count($this->list_items);
  352. //Get the calls destination wise and calculate total cost
  353. if (is_array($this->list_total_destination)) {
  354. foreach ($this->list_total_destination as $data)
  355. $this->list_items[] = Array(
  356. gettext('Calls'), // invoicesection
  357. $data[0], // designation
  358. '', // sub_designation
  359. '', // start_date
  360. '', // end_date
  361. '', // bill_date
  362. $data[1], // calltime
  363. $data[3], // nbcalls
  364. 1, // quantity
  365. convert_currency($this->currencies_list, $data[4], strtoupper($this->base_currency), strtoupper($this->invoice_currency)), // buy_price
  366. convert_currency($this->currencies_list, $data[2], strtoupper($this->base_currency), strtoupper($this->invoice_currency)) // price
  367. );
  368. } else throw new Exception('Unable to generate Call Items because Calls were not listed');
  369. if ($this->verbose_level >= 1) echo "\n".(count($this->list_items) - $nb_items).' call items generated';
  370. }
  371. // Add Charge Items
  372. function GenerateChargeItems() {
  373. if (! $this->IsCoveringCharges()) {
  374. if ($this->verbose_level >= 1) echo "\nCharges not covered. Skipping ChargeItems generation.";
  375. return;
  376. }
  377. global $chargetype_list;
  378. $nb_items = count($this->list_items);
  379. //Get the calls destination wise and calculate total cost
  380. if (is_array($this->list_total_charge)) {
  381. foreach ($this->list_total_charge as $data) {
  382. $chargeitem = Array(
  383. $chargetype_list[$data[6]], // invoicesection
  384. ($data[11] > 0)? $data[13] : $data[7], // designation
  385. ($data[11] > 0)? $data[12] : '', // sub_designation
  386. $data[10], // start_date
  387. $data[9], // end_date
  388. $data[3], // bill_date
  389. 0, // calltime
  390. 0, // nbcalls
  391. 1, // quantity
  392. 0, // buy_price
  393. convert_currency($this->currencies_list, $data[4], strtoupper($data[5]), strtoupper($this->invoice_currency)) // price
  394. );
  395. // look for identical item
  396. $found = false;
  397. foreach ($this->list_items as &$item)
  398. if ($found = ($item[0] == $chargeitem[0]
  399. && $item[1] == $chargeitem[1]
  400. && $item[3] == $chargeitem[3]
  401. && $item[4] == $chargeitem[4]
  402. && $item[9] == $chargeitem[9]
  403. && $item[10]== $chargeitem[10]))
  404. {
  405. $item[2] = '';
  406. $item[8]++;
  407. if ($this->verbose_level >= 2) {
  408. echo "\n #Similar Item: ";
  409. print_r($item);
  410. }
  411. break;
  412. }
  413. if (! $found) {
  414. if ($this->verbose_level >= 2) {
  415. echo "\n #Item: ";
  416. print_r($chargeitem);
  417. }
  418. $this->list_items[] = $chargeitem;
  419. }
  420. }
  421. } else throw new Exception('Unable to generate Call Items because Calls were not listed');
  422. if ($this->verbose_level >= 1) echo "\n".(count($this->list_items) - $nb_items).' charge items generated';
  423. }
  424. // Sort Items according to their category: generate $list_category_items with $list_items
  425. function SortItems() {
  426. $this->list_category_items = Array();
  427. foreach($this->list_items as &$item) {
  428. // name fiels
  429. list($item['invoicesection'],$item['designation'],$item['sub_designation'],$item['start_date'],$item['end_date'],
  430. $item['bill_date'],$item['calltime'],$item['nbcalls'],$item['quantity'],$item['buy_price'], $item['price']) = $item;
  431. // sort it in correct category
  432. $this->list_category_items[$item[0]][] = $item;
  433. }
  434. if ($this->verbose_level >= 3) print_r($this->list_category_items);
  435. }
  436. // Sum all invoice Items. Compute tax
  437. function ComputeTotal() {
  438. $this->invoice_subtotal = 0;
  439. foreach($this->list_items as $item)
  440. $this->invoice_subtotal += $item[8] * $item[10]; // quantity * price
  441. $this->invoice_tax = $this->invoice_subtotal * $this->customer_VAT / 100;
  442. $this->invoice_total = $this->invoice_subtotal + $this->invoice_tax;
  443. if ($this->verbose_level >=1)
  444. echo "\nTotal $this->invoice_subtotal + VAT( $this->customer_VAT % ) : $this->invoice_tax = $this->invoice_total $this->invoice_currency";
  445. }
  446. ////
  447. //// Bill Invoice
  448. ////
  449. // Write Invoice & Items
  450. // If minimal amount is reached
  451. function BillInvoice($enable_minimal_amount = false, $minimal_amount = 0, $custom_template = '') {
  452. if ($enable_minimal_amount == true && $this->invoice_total < $minimal_amount) {
  453. if ($this->verbose_level >=1) echo "\nAmount $this->invoice_total < $minimal_amount , skipping this invoice";
  454. return;
  455. }
  456. if (! $this->IsOfficial())
  457. throw new Exception('Cannot bill invoice that filters calls');
  458. $this->billedinvoice_id = 0;
  459. $this->billedinvoice_filename = '';
  460. $this->billedinvoice_paymentstatus = 0;
  461. $this->billedinvoice_creationdate = date('c');
  462. $this->billedinvoice_default_template = ($custom_template == '')? $this->customer_invoicetemplate : $custom_template;
  463. if ($this->verbose_level >= 1) echo "\n Template for this invoice is:".$this->billedinvoice_default_template;
  464. $this->WriteInvoice();
  465. }
  466. // Write Invoice in Database
  467. function WriteInvoice() {
  468. if (! $this->IsBilled())
  469. throw new Exception('Cannot write an invoice that is not billed');
  470. // Here we have to Create a Insert Statement to insert Records into the Invoices Table.
  471. $Query_Invoices = "INSERT INTO cc_invoice (".
  472. " cardid, invoicecreated_date, amount, tax, total, filename, payment_status,".
  473. " cover_call_startdate, cover_call_enddate, cover_charge_startdate, cover_charge_enddate,".
  474. " currency, previous_balance, current_balance, templatefile, ".
  475. " username, lastname, firstname, address, city, state, country, ".
  476. " zipcode, phone, email, fax, vat ".
  477. " ) VALUES (".
  478. "'$this->customer_cardid', '$this->billedinvoice_creationdate', $this->invoice_subtotal, $this->invoice_tax, $this->invoice_total, '$this->billedinvoice_filename', $this->billedinvoice_paymentstatus,".
  479. "'$this->cover_call_startdate', '$this->cover_call_enddate', '$this->cover_charge_startdate', '$this->cover_charge_enddate',".
  480. "'$this->invoice_currency', '$this->previous_balance', $this->customer_currentbalance, '$this->billedinvoice_default_template',".
  481. "'$this->customer_username', '$this->customer_lastname', '$this->customer_firstname', '$this->customer_address', '$this->customer_city', '$this->customer_state', '$this->customer_country',".
  482. "'$this->customer_zipcode','$this->customer_phone','$this->customer_email','$this->customer_fax','$this->customer_VAT');";
  483. if ($this->verbose_level >= 1) echo "\n Query_Write_Invoices = $Query_Invoices \n";
  484. if (! $this->instance_table -> SQLExec ($this->DB_Handle, $Query_Invoices, 0))
  485. throw new Exception('Failed to write invoice in Database');
  486. $QUERY = "Select Max(id) from cc_invoice";
  487. $result = $this->instance_table -> SQLExec ($this->DB_Handle, $QUERY);
  488. if (! is_array($result) || count($result) == 0)
  489. throw new Exception('Unable to find invoice again');
  490. $this->billedinvoice_id = $result[0][0];
  491. if (count($this->list_items)) {
  492. $QUERY = "INSERT INTO cc_invoice_items(invoiceid, invoicesection, designation, sub_designation, start_date, end_date, bill_date, calltime, nbcalls, quantity, buy_price, price) VALUES ";
  493. $first = true;
  494. foreach($this->list_items as $item) {
  495. if (! $first) $QUERY .= ", ";
  496. $QUERY .= "('$this->billedinvoice_id','$item[0]','$item[1]','$item[2]','$item[3]','$item[4]','$item[5]','$item[6]','$item[7]','$item[8]','$item[9]','$item[10]')";
  497. $first = false;
  498. }
  499. if ($this->verbose_level >= 2) echo "\n QUERY_WRITE_ITEMS = $QUERY";
  500. if (! $this->instance_table-> SQLExec ($this->DB_Handle, $QUERY,0))
  501. throw new Exception('Failed to write invoice items in Database');
  502. } else if ($this->verbose_level >= 1) echo "\n no items in this invoice";
  503. if ($this->verbose_level >= 1) echo "\n ################################################################################# \n\n";
  504. }
  505. ////
  506. //// Load Billed Invoice
  507. ////
  508. // Fills its attributes according to database
  509. function LoadInvoice($invoice_id) {
  510. $this->billedinvoice_id = $invoice_id;
  511. $this->list_total_charge = NULL;
  512. $this->list_total_destination = NULL;
  513. // First get info about invoice
  514. $QUERY = "SELECT cardid, invoicecreated_date, amount, tax, total, filename, payment_status,".
  515. " cover_call_startdate, cover_call_enddate, cover_charge_startdate, cover_charge_enddate,".
  516. " currency, previous_balance, current_balance, templatefile, ".
  517. " username, lastname, firstname, address, city, state, country, ".
  518. " zipcode, phone, email, fax, vat ".
  519. " FROM cc_invoice WHERE id = $invoice_id";
  520. if ($this->verbose_level>=1) echo "\nQUERY_INVOICE = $QUERY";
  521. $resinvoice = $this->instance_table -> SQLExec ($this-> DB_Handle, $QUERY);
  522. if (!is_array($resinvoice) || count($resinvoice) != 1)
  523. throw new Exception("\nInvoice with id=".$this->invoice_id." not found.");
  524. if ($this->verbose_level>=2) print_r($resinvoice);
  525. // TODO: réecrire avec list(...) = $resinvoice[0]
  526. $this->customer_cardid = $resinvoice[0][0];
  527. $this->billedinvoice_creationdate = $resinvoice[0][1];
  528. $this->invoice_subtotal = $resinvoice[0][2];
  529. $this->invoice_tax = $resinvoice[0][3];
  530. $this->invoice_total = $resinvoice[0][4];
  531. $this->billedinvoice_filename = $resinvoice[0][5];
  532. $this->billedinvoice_paymentstatus = $resinvoice[0][6];
  533. $this->cover_call_startdate = $resinvoice[0][7];
  534. $this->cover_call_enddate = $resinvoice[0][8];
  535. $this->cover_charge_startdate = $resinvoice[0][9];
  536. $this->cover_charge_enddate = $resinvoice[0][10];
  537. $this->invoice_currency = $resinvoice[0][11];
  538. $this->previous_balance = $resinvoice[0][12];
  539. $this->customer_currentbalance = $resinvoice[0][13];
  540. $this->billedinvoice_default_template = $resinvoice[0]['templatefile'];
  541. $this->customer_username = $resinvoice[0][15];
  542. $this->customer_lastname = $resinvoice[0][16];
  543. $this->customer_firstname = $resinvoice[0][17];
  544. $this->customer_address = $resinvoice[0][18];
  545. $this->customer_city = $resinvoice[0][19];
  546. $this->customer_state = $resinvoice[0][20];
  547. $this->customer_country = $resinvoice[0][21];
  548. $this->customer_zipcode = $resinvoice[0][22];
  549. $this->customer_phone = $resinvoice[0][23];
  550. $this->customer_email = $resinvoice[0][24];
  551. $this->customer_fax = $resinvoice[0][25];
  552. $this->customer_VAT = $resinvoice[0][26];
  553. // get invoice items
  554. $QUERY = "SELECT invoicesection, designation, sub_designation, start_date, end_date, bill_date, calltime, nbcalls, quantity, buy_price, price".
  555. " FROM cc_invoice_items WHERE invoiceid = $this->billedinvoice_id";
  556. if ($this->verbose_level >= 1) echo "\n QUERY_READ_ITEMS = $QUERY";
  557. $this->list_items = $this->instance_table-> SQLExec ($this->DB_Handle, $QUERY);
  558. if (!is_array($this->list_items))
  559. throw new Exception("Cannot list invoice items for invoice id=".$this->billedinvoice_id);
  560. if ($this->verbose_level >= 2) print_r($this->list_items);
  561. // Sort items
  562. $this->SortItems();
  563. }
  564. ////
  565. //// Display Invoice
  566. ////
  567. // Gives default values if some fiels are left blank
  568. function GetTemplateFullPath($invoicetype, $template) {
  569. global $A2B;
  570. if ($template == '') {
  571. if ($this->billedinvoice_default_template != '')
  572. $template = $this->billedinvoice_default_template;
  573. else
  574. switch ($invoicetype) {
  575. case 'billed':
  576. $template = $this->customer_invoicetemplate;
  577. break;
  578. case 'outstanding':
  579. $template = $this->customer_outstandingtemplate;
  580. break;
  581. default:
  582. throw new Exception('Unable to choose a template file');
  583. }
  584. }
  585. switch ($invoicetype) {
  586. case 'billed':
  587. return $A2B->config['global']['invoice_template_path'].$template;
  588. case 'outstanding':
  589. return $A2B->config['global']['outstanding_template_path'].$template;
  590. case 'sales':
  591. return $A2B->config['global']['sales_template_path'].$template;
  592. default:
  593. throw new Exception('Unknown invoice type');
  594. }
  595. }
  596. function DisplayHTML($smarty, $invoicetype, $template = '') {
  597. $smarty->assign("invoice", $this);
  598. $smarty->debugging = ($this->verbose_level);
  599. $template_path = $this->GetTemplateFullPath($invoicetype, $template);
  600. if ($this->verbose_level >= 1)
  601. echo "\nTemplate Path: ".$template_path;
  602. $smarty->display($template_path);
  603. }
  604. function GetHTML($smarty, $invoicetype, $template = '') {
  605. $smarty->assign("invoice", $this);
  606. $smarty->debugging = ($this->verbose_level);
  607. $template_path = $this->GetTemplateFullPath($invoicetype, $template);
  608. if ($this->verbose_level >= 1)
  609. echo "\nTemplate Path: ".$template_path;
  610. return $smarty->fetch($template_path);
  611. }
  612. function GetPDF($smarty, $invoicetype, $template = '') {
  613. require_once('pdf-invoices/html2pdf/html2fpdf.php');
  614. $pdf = new HTML2FPDF();
  615. $pdf -> DisplayPreferences('HideWindowUI');
  616. $pdf -> AddPage();
  617. $pdf -> WriteHTML($this->GetHTML($smarty, $invoicetype, $template));
  618. return $pdf->Output('Invoice_'.date("d/m/Y-H:i").'.pdf', 'S');
  619. }
  620. function DisplayPDF($smarty, $invoicetype, $template) {
  621. require_once('pdf-invoices/html2pdf/html2fpdf.php');
  622. $pdf = new HTML2FPDF();
  623. $pdf -> DisplayPreferences('HideWindowUI');
  624. $pdf -> AddPage();
  625. $pdf -> WriteHTML($this->GetHTML($smarty, $invoicetype, $template));
  626. // TODO: find a better name
  627. return $pdf->Output('Invoice_'.date("d/m/Y-H:i").'.pdf', 'I');
  628. }
  629. function SendEMail($smarty, $template = '') {
  630. // Render Invoice to a PDF
  631. $stream = $this->GetPDF($smarty, 'billed', $template);
  632. // Get Mail template
  633. $QUERY = "SELECT mailtype, fromemail, fromname, subject, messagetext, messagehtml FROM cc_templatemail WHERE mailtype='invoice' ";
  634. if($this->verbose_level >= 1)
  635. echo "\nQuery Mail Template : $QUERY";
  636. $res = $this->instance_table -> SQLExec ($this->DB_Handle, $QUERY);
  637. if (!is_array($res) || count($res) != 1)
  638. throw new Exception("\nUnable to find mail template=".$this->invoice_id." not found.");
  639. list($mailtype, $from, $fromname, $subject, $messagetext, $messagehtml) = $res [0];
  640. // Sent Email
  641. $ok = send_email_attachment($from, $this->customer_email, $subject, $messagetext,'Invoice_'.date("d/m/Y-H:i").'.pdf', $stream );
  642. if ($this->invoice_id) {
  643. // Write it in invoice history
  644. $currentdate = date("Y-m-d h:i:s");
  645. $QUERY = "INSERT INTO cc_invoice_history (invoiceid,invoicesent_date,invoicestatus) VALUES('$this->invoice_id', '$currentdate', '".(($ok)?1:0)."')";
  646. $this->instance_table -> SQLExec ($this->DB_Handle, $QUERY,0);
  647. if($this->verbose_level >= 1)
  648. echo "\nWrite Invoice History : $QUERY";
  649. }
  650. return (($ok)? true : false);
  651. }
  652. }
  653. ?>