Commit 7962106c authored by Karamel's avatar Karamel
Browse files

Fix fix about Doctrine and the new embedded objects. Add http ticket tests.

parent bc714f59
......@@ -14,3 +14,6 @@ database/password = "password"
http/host = "http://localhost"
http/user = "test"
http/password = "pasteque"
; Phpunit uses it's own default timezone. Set your server timezone
; for local checks vs http calls.
date/timezone = "Europe/Berlin"
......@@ -178,6 +178,10 @@ class Ticket extends DoctrineModel
$this->lines->add($line);
$line->setTicket($this);
}
public function removeLine($line) {
$this->lines->removeElement($line);
$line->setTicket(null);
}
/**
* Array of tax totals. It holds the final tax base/amount for each
......@@ -201,6 +205,10 @@ class Ticket extends DoctrineModel
$this->taxes->add($tax);
$tax->setTicket($this);
}
public function removeTax($tax) {
$this->taxes->removeElement($tax);
$tax->setTicket(null);
}
/**
* Array of payment
......@@ -223,6 +231,10 @@ public function getPayments() { return $this->payments; }
$this->payments->add($payment);
$payment->setTicket($this);
}
public function removePayment($payment) {
$this->payments->removeElement($payment);
$payment->setTicket(null);
}
/**
* Id of the customer
......
......@@ -119,6 +119,36 @@ abstract class DoctrineModel
return $data;
}
/** Find the name of the method to operate the given field.
* @param $operation The name of the operation to execute ('set', 'add' etc)
* @param $fieldName The name of the field.
* @return The name of the function. Null if not found. */
protected function findMethodName($operation, $fieldName) {
$methodName = $operation . ucfirst($fieldName);
if (is_callable([$this, $methodName])) {
return $methodName;
}
// Maybe field is plural (i.e. addLine for lines)
$methodName = substr($methodName, 0, -1);
if (is_callable([$this, $methodName])) {
return $methodName;
}
// Maybe field is plural with 'e' (i.e. addTax for taxes)
$methodName = substr($methodName, 0, -1);
if (is_callable([$this, $methodName])) {
return $methodName;
}
return null;
}
/** Call findMethodName and throw a ReflectionException if no method is found. */
protected function findMethodNameOrThrow($operation, $fieldName) {
$methodName = $this->findMethodName($operation, $fieldName);
if ($methodName == null) {
throw new \ReflectionException(sprintf('Method to %s %s on %s was not found', $operation, $fieldName, static::class));
}
return $methodName;
}
/** Build an instance from struct. If id is set, it will ask the DAO to set
* the current values. If not a new instance is created and returned.
* @throws \UnexpectedValueException If id is set but no data is found. */
......@@ -146,7 +176,8 @@ abstract class DoctrineModel
}
if (array_key_exists($field, $struct)) {
$value = $struct[$field];
call_user_func(array($this, 'set' . ucfirst($field)), $struct[$field]);
$setter = $this->findMethodNameOrThrow('set', $field);
call_user_func([$this, $setter], $struct[$field]);
}
}
}
......@@ -221,7 +252,8 @@ abstract class DoctrineModel
$toAdd = [];
$toRemove = [];
// Look for records to remove
$dbValues = call_user_func(array($model, 'get' . ucfirst($fieldName)));
$getter = $model->findMethodNameOrThrow('get', $fieldName);
$dbValues = call_user_func([$model, $getter]);
foreach ($dbValues as $dbv) {
$found = false;
foreach ($value as $v) {
......@@ -256,35 +288,27 @@ abstract class DoctrineModel
}
}
// Add and remove
$remover = $model->findMethodNameOrThrow('remove', $fieldName);
foreach ($toRemove as $rm) {
$methodName = 'remove' . ucfirst($fieldName);
if (is_callable([$model, $methodName])) {
call_user_func([$model, $methodeName], $rm);
} else {
$methodName = substr($methodName, 0, -1);
call_user_func([$model, $methodName], $rm);
}
call_user_func([$model, $remover], $rm);
}
$adder = $model->findMethodNameOrThrow('add', $fieldName);
foreach ($toAdd as $add) {
$methodName = 'add' . ucfirst($fieldName);
if (is_callable([$model, $methodName])) {
call_user_func([$model, $methodName], $add);
} else {
$methodName = substr($methodName, 0, -1);
call_user_func([$model, $methodName], $add);
}
call_user_func([$model, $adder], $add);
}
} else {
call_user_func(array($model, 'set' . ucfirst($fieldName)), $value);
$setter = $model->findMethodNameOrThrow('set', $fieldName);
call_user_func([$model, $setter], $value);
}
} else if (isset($struct[$fieldName]) && $struct[$fieldName] === null) {
$setter = $model->findMethodNameOrThrow('set', $fieldName);
// Delete association on explicit null value
if (empty($field['array'])) {
// Erase association for empty values
call_user_func(array($model, 'set' . ucfirst($fieldName)), null);
call_user_func([$model, $setter], null);
} else {
// Clear array for arrays
call_user_func(array($model, 'set' . ucfirst($fieldName)), []);
call_user_func([$model, $setter], []);
}
} // Empty an not null: keep the current association value
}
......
......@@ -44,6 +44,7 @@ use \Pasteque\Server\System\DAO\DAOFactory;
use \PHPUnit\Framework\TestCase;
require_once(dirname(dirname(__FILE__)) . "/common_load.php");
require_once(dirname(dirname(__FILE__)) . '/common_ticket.php');
/** TicketAPI with access to protected methods. */
class TicketAPIProxy extends TicketAPI
......@@ -132,6 +133,28 @@ class TicketAPITest extends TestCase
$this->dao->close();
}
private function getBaseTicket($number) {
// Proxy for common_ticket
return getBaseTicket($this->cash, $this->session, $number, $this->user);
}
private function getBaseLine($dispOrder, $label, $taxedPrice) {
// Proxy for common_ticket
return getBaseLine($this->tax, $dispOrder, $label, $taxedPrice);
}
private function getBaseTax($base, $amount) {
// Proxy for common_ticket
return getBaseTax($this->tax, $base, $amount);
}
private function getBasePayment($dispOrder, $amount) {
// Proxy for common_ticket
return getBasePayment($this->pm, $this->curr, $dispOrder, $amount);
}
public function testUpdateEOSEmpty() {
$this->api->updateEOSTicketProxy(null);
$this->dao->commit();
......@@ -267,15 +290,9 @@ class TicketAPITest extends TestCase
'currencyAmount' => 11.0], $this->dao);
$tkt->addPayment($pm1);
$this->api->write($tkt);
$readTkt = $this->dao->read(Ticket::class, $tkt->getId());
$readTkt = $this->dao->readSnapshot(Ticket::class, $tkt->getId());
$this->assertNotNull($readTkt);
$this->assertEquals($this->cash->getId(), $readTkt->getCashRegister()->getId());
$this->assertEquals($this->session->getSequence(), $readTkt->getSequence());
$this->assertEquals(1, $readTkt->getNumber());
$this->assertNull($readTkt->getPrice());
$this->assertEquals(11.0, $readTkt->getTaxedPrice());
$this->assertEquals(10.0, $readTkt->getFinalPrice());
$this->assertEquals(11.0, $readTkt->getFinalTaxedPrice());
assertTicketModelEqModel($tkt, $readTkt, $this);
$readfTkt = $this->dao->read(FiscalTicket::class, [
'type' => FiscalTicket::TYPE_TICKET,
'sequence' => FiscalTicket::getTicketSequence($readTkt),
......
......@@ -33,6 +33,10 @@ $dbInfo = ['type' => $cfg['database/type'], 'host' => $cfg['database/host'],
'port' => $cfg['database/port'], 'name' => $cfg['database/name'],
'user' => $cfg['database/user'], 'password' => $cfg['database/password']];
if (!empty($cfg['date/timezone'])) {
date_default_timezone_set($cfg['date/timezone']);
}
function apiUrl($target) {
global $cfg;
if (substr($cfg['http/host'], -1) == '/') {
......
<?php
// Pastèque Web back office
//
// Copyright (C) 2013 Scil (http://scil.coop)
//
// This file is part of Pastèque.
//
// Pastèque is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pastèque is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pastèque. If not, see <http://www.gnu.org/licenses/>.
// Some utility functions to test tickets.
use \Pasteque\Server\Model\Ticket;
use \Pasteque\Server\System\DateUtils;
use \Pasteque\Server\System\DAO\DAOCondition;
function getBaseTicket($cash, $session, $number, $user) {
$date = DateUtils::readString('2018-01-01 10:05');
$date->add(new \DateInterval('PT' . $number . 'M'));
return ['cashRegister' => $cash->getId(),
'sequence' => $session->getSequence(),
'number' => $number,
'date' => DateUtils::toTimestamp($date),
'custCount' => null,
'price' => 10.0,
'taxedPrice' => 11.0,
'discountRate' => 0.0,
'finalPrice' => 10.0,
'finalTaxedPrice' => 11.0,
'custBalance' => 0.0,
'user' => $user->getId(),
'lines' => [],
'taxes' => [],
'payments' => [],
'customer' => null,
'tariffArea' => null,
'discountProfile' => null];
}
function getBaseLine($tax, $dispOrder, $label, $price) {
return ['dispOrder' => $dispOrder, 'productLabel' => $label,
'unitPrice' => null, 'taxedUnitPrice' => $price,
'quantity' => 1, 'price' => null, 'taxedPrice' => $price,
'taxRate' => 0.1, 'discountRate' => 0.0,
'tax' => $tax->getId(),
'finalPrice' => null, 'finalTaxedPrice' => $price];
}
function getBaseTax($tax, $base, $amount) {
return ['base' => $base, 'amount' => $amount, 'taxRate' => 0.1,
'tax' => $tax->getId()];
}
function getBasePayment($paymentMode, $currency, $dispOrder, $amount) {
return ['dispOrder' => $dispOrder, 'amount' => $amount,
'currencyAmount' => $amount,
'paymentMode' => $paymentMode->getId(),
'currency' => $currency->getId()];
}
/** Check equality of a ticket model and a ticket structure.i
* Should be used with a model and a dao snapshot to prevent mixing references.
* @param $ut Unit Test ($this in test classes). */
function assertTicketModelEqModel($m1, $m2, $ut) {
$ut->assertEquals($m1->getCashRegister()->getId(), $m2->getCashRegister()->getId());
$ut->assertEquals($m1->getSequence(), $m2->getSequence());
$ut->assertEquals($m1->getNumber(), $m2->getNumber());
$ut->assertEquals($m1->getDate()->format('Y-m-d H:i:s'), $m2->getDate()->format('Y-m-d H:i:s'));
$ut->assertEquals($m1->getUser()->getId(), $m2->getUser()->getId());
// Check lines
$ut->assertEquals(count($m1->getLines()), count($m2->getLines()));
for ($i = 0; $i < count($m1->getLines()); $i++) {
$m1Line = $m1->getLines()->get($i);
$m2Line = $m2->getLines()->get($i);
$ut->assertEquals($m1Line->getDispOrder(), $m2Line->getDispOrder());
$ut->assertEquals($m1Line->getProductLabel(), $m2Line->getProductLabel());
$ut->assertEquals($m1Line->getUnitPrice(), $m2Line->getUnitPrice());
$ut->assertEquals($m1Line->getTaxedUnitPrice(), $m2Line->getTaxedUnitPrice());
$ut->assertEquals($m1Line->getQuantity(), $m2Line->getQuantity());
$ut->assertEquals($m1Line->getPrice(), $m2Line->getPrice());
$ut->assertEquals($m1Line->getTaxedPrice(), $m2Line->getTaxedPrice());
$ut->assertEquals($m1Line->getTaxRate(), $m2Line->getTaxRate());
$ut->assertEquals($m1Line->getDiscountRate(), $m2Line->getDiscountRate());
$ut->assertEquals($m1Line->getFinalPrice(), $m2Line->getFinalPrice());
$ut->assertEquals($m1Line->getFinalTaxedPrice(), $m2Line->getFinalTaxedPrice());
$prd1 = $m1Line->getProduct() !== null ? $m1Line->getProduct()->getId() : null;
$prd2 = $m2Line->getProduct() !== null ? $m2Line->getProduct()->getId() : null;
$ut->assertEquals($prd1, $prd2);
$tax1 = $m1Line->getTax() !== null ? $m1Line->getTax()->getId() : null;
$tax2 = $m2Line->getTax() !== null ? $m2Line->getTax()->getId() : null;
$ut->assertEquals($tax1, $tax2);
}
// Check tax lines
$ut->assertEquals(count($m1->getTaxes()), count($m2->getTaxes()));
for ($i = 0; $i < count($m1->getTaxes()); $i++) {
$m1Tax = $m1->getTaxes()->get($i);
$m2Tax = $m2->getTaxes()->get($i);
$ut->assertEquals($m1Tax->getTax()->getId(), $m2Tax->getTax()->getId());
$ut->assertEquals($m1Tax->getTaxRate(), $m2Tax->getTaxRate());
$ut->assertEquals($m1Tax->getBase(), $m2Tax->getBase());
$ut->assertEquals($m1Tax->getAmount(), $m2Tax->getAmount());
}
// Check payments
$ut->assertEquals(count($m1->getPayments()), count($m2->getPayments()));
for ($i = 0; $i < count($m2->getPayments()); $i++) {
$m1Pay = $m1->getPayments()->get($i);
$m2Pay = $m2->getPayments()->get($i);
$ut->assertEquals($m1Pay->getPaymentMode()->getId(), $m2Pay->getPaymentMode()->getId());
$ut->assertEquals($m1Pay->getCurrency()->getId(), $m2Pay->getCurrency()->getId());
$ut->assertEquals($m1Pay->getAmount(), $m2Pay->getAmount());
$ut->assertEquals($m1Pay->getCurrencyAmount(), $m2Pay->getCurrencyAmount());
}
}
/** Check equality of a ticket model and a ticket structure.
* @param $ut Unit Test ($this in test classes). */
function assertTicketModelEqStruct($model, $struct, $ut) {
// Check direct fields
$ut->assertEquals($model->getCashRegister()->getId(), $struct['cashRegister']);
$ut->assertEquals($model->getSequence(), $struct['sequence']);
$ut->assertEquals($model->getNumber(), $struct['number']);
$ut->assertEquals($model->getDate()->format('Y-m-d H:i:s'), DateUtils::readDate($struct['date'])->format('Y-m-d H:i:s'));
$ut->assertEquals($model->getUser()->getId(), $struct['user']);
// Check lines
$ut->assertEquals(count($model->getLines()), count($struct['lines']));
for ($i = 0; $i < count($struct['lines']); $i++) {
$mLine = $model->getLines()->get($i);
$sLine = $struct['lines'][$i];
$ut->assertEquals($mLine->getDispOrder(), $sLine['dispOrder']);
$ut->assertEquals($mLine->getProductLabel(), $sLine['productLabel']);
$ut->assertEquals($mLine->getUnitPrice(), $sLine['unitPrice']);
$ut->assertEquals($mLine->getTaxedUnitPrice(), $sLine['taxedUnitPrice']);
$ut->assertEquals($mLine->getQuantity(), $sLine['quantity']);
$ut->assertEquals($mLine->getPrice(), $sLine['price']);
$ut->assertEquals($mLine->getTaxedPrice(), $sLine['taxedPrice']);
$ut->assertEquals($mLine->getTaxRate(), $sLine['taxRate']);
$ut->assertEquals($mLine->getDiscountRate(), $sLine['discountRate']);
$ut->assertEquals($mLine->getFinalPrice(), $sLine['finalPrice']);
$ut->assertEquals($mLine->getFinalTaxedPrice(), $sLine['finalTaxedPrice']);
$prd = $mLine->getProduct() !== null ? $mLine->getProduct()->getId() : null;
$ut->assertEquals($prd, (isset($sLine['product']) ? $sLine['product'] : null));
$tax = $mLine->getTax() !== null ? $mLine->getTax()->getId() : null;
$ut->assertEquals($tax, (isset($sLine['tax']) ? $sLine['tax'] : null));
}
// Check tax lines
$ut->assertEquals(count($model->getTaxes()), count($struct['taxes']));
for ($i = 0; $i < count($struct['taxes']); $i++) {
$mTax = $model->getTaxes()->get($i);
$sTax = $struct['taxes'][$i];
$ut->assertEquals($mTax->getTax()->getId(), $sTax['tax']);
$ut->assertEquals($mTax->getTaxRate(), $sTax['taxRate']);
$ut->assertEquals($mTax->getBase(), $sTax['base']);
$ut->assertEquals($mTax->getAmount(), $sTax['amount']);
}
// Check payments
$ut->assertEquals(count($model->getPayments()), count($struct['payments']));
for ($i = 0; $i < count($struct['payments']); $i++) {
$mPay = $model->getPayments()->get($i);
$sPay = $struct['payments'][$i];
$ut->assertEquals($mPay->getPaymentMode()->getId(), $sPay['paymentMode']);
$ut->assertEquals($mPay->getCurrency()->getId(), $sPay['currency']);
$ut->assertEquals($mPay->getAmount(), $sPay['amount']);
$ut->assertEquals($mPay->getCurrencyAmount(), $sPay['currencyAmount']);
}
}
function readTicketSnapshot($cashRegister, $sequence, $number, $dao) {
$search = $dao->search(Ticket::class,
[new DAOCondition('cashRegister', '=', $cashRegister),
new DAOCondition('sequence', '=', $sequence),
new DAOCondition('number', '=', $number)]);
if (count($search) > 0) {
return $dao->readSnapshot(Ticket::class, $search[0]->getId());
}
return null;
}
......@@ -39,10 +39,12 @@ use \Pasteque\Server\Model\TicketPayment;
use \Pasteque\Server\Model\User;
use \Pasteque\Server\System\DateUtils;
use \Pasteque\Server\System\Login;
use \Pasteque\Server\System\DAO\DAOCondition;
use \Pasteque\Server\System\DAO\DAOFactory;
use \PHPUnit\Framework\TestCase;
require_once(dirname(dirname(__FILE__)) . "/common_load.php");
require_once(dirname(dirname(__FILE__)) . "/common_ticket.php");
class HttpTicketTest extends TestCase
{
......@@ -136,44 +138,28 @@ class HttpTicketTest extends TestCase
}
private function getBaseTicket($number) {
$date = DateUtils::readString('2018-01-01 10:05');
$date->add(new \DateInterval('PT' . $number . 'M'));
return ['cashRegister' => $this->cash->getId(),
'sequence' => $this->session->getSequence(),
'number' => $number,
'date' => DateUtils::toTimestamp($date),
'custCount' => null,
'price' => 10.0,
'taxedPrice' => 11.0,
'discountRate' => 0.0,
'finalPrice' => 10.0,
'finalTaxedPrice' => 11.0,
'custBalance' => 0.0,
'user' => $this->user->getId(),
'lines' => [],
'taxes' => [],
'payments' => [],
'customer' => null,
'tariffArea' => null,
'discountProfile' => null];
// Proxy for common_ticket
return getBaseTicket($this->cash, $this->session, $number, $this->user);
}
private function getBaseLine($dispOrder, $label, $price) {
return ['dispOrder' => $dispOrder, 'productLabel' => $label,
'unitPrice' => null, 'taxedUnitPrice' => $price,
'quantity' => 1, 'price' => null, 'taxedPrice' => $price,
'taxRate' => 0.1, 'discountRate' => 0.0,
'tax' => $this->tax->getId(),
'finalPrice' => null, 'finalTaxedPrice' => $price];
private function getBaseLine($dispOrder, $label, $taxedPrice) {
// Proxy for common_ticket
return getBaseLine($this->tax, $dispOrder, $label, $taxedPrice);
}
private function getBaseTax($base, $amount) {
return ['base' => $base, 'amount' => $amount, 'taxRate' => 0.1,
'tax' => $this->tax->getId()];
// Proxy for common_ticket
return getBaseTax($this->tax, $base, $amount);
}
private function getBasePayment($dispOrder, $amount) {
return ['dispOrder' => $dispOrder, 'amount' => $amount,
'currencyAmount' => $amount,
'paymentMode' => $this->pm->getId(),
'currency' => $this->curr->getId()];
// Proxy for common_ticket
return getBasePayment($this->pm, $this->curr, $dispOrder, $amount);
}
private function assertTicketModelEqStruct($model, $struct) {
// Proxy for common_ticket
assertTicketModelEqStruct($model, $struct, $this);
}
public function testEmpty() {
......@@ -206,6 +192,8 @@ class HttpTicketTest extends TestCase
$this->assertEquals($this->cash->getId(), $s['cashRegister'], $resp);
$this->assertEquals($this->session->getSequence(), $s['sequence'], $resp);
$this->assertEquals(1, $s['number'], $resp);
$snap = readTicketSnapshot($this->cash, $s['sequence'], $s['number'], $this->dao);
$this->assertTicketModelEqStruct($snap, $tkt);
$this->assertEquals(0, count($data['failures']), $resp);
$this->assertEquals(0, count($data['errors']), $resp);
}
......@@ -238,12 +226,18 @@ class HttpTicketTest extends TestCase
$this->assertEquals($this->cash->getId(), $s1['cashRegister'], $resp);
$this->assertEquals($this->session->getSequence(), $s1['sequence'], $resp);
$this->assertEquals(1, $s1['number'], $resp);
$snap1 = readTicketSnapshot($this->cash, $s1['sequence'], $s1['number'], $this->dao);
$this->assertTicketModelEqStruct($snap1, $tkt);
$this->assertEquals($this->cash->getId(), $s2['cashRegister'], $resp);
$this->assertEquals($this->session->getSequence(), $s2['sequence'], $resp);
$this->assertEquals(2, $s2['number'], $resp);
$snap2 = readTicketSnapshot($this->cash, $s2['sequence'], $s2['number'], $this->dao);
$this->assertTicketModelEqStruct($snap2, $tkt2);
$this->assertEquals($this->cash->getId(), $s3['cashRegister'], $resp);
$this->assertEquals($this->session->getSequence(), $s3['sequence'], $resp);
$this->assertEquals(3, $s3['number'], $resp);
$snap3 = readTicketSnapshot($this->cash, $s3['sequence'], $s3['number'], $this->dao);
$this->assertTicketModelEqStruct($snap3, $tkt3);
$this->assertEquals(0, count($data['failures']), $resp);
$this->assertEquals(0, count($data['errors']), $resp);
}
......@@ -269,7 +263,9 @@ class HttpTicketTest extends TestCase
$s = $data['successes'][0];
$this->assertEquals($this->cash->getId(), $s['cashRegister'], $resp);
$this->assertEquals($this->session->getSequence(), $s['sequence'], $resp);
$this->assertEquals(1, $s['number'], $resp);
$this->assertEquals(1, $s['number'], $resp);
$snap1 = readTicketSnapshot($this->cash, $s['sequence'], $s['number'], $this->dao);
$this->assertTicketModelEqStruct($snap1, $tkt);
$this->assertEquals(1, count($data['failures']), $resp);
$r = $data['failures'][0];
$this->assertEquals($this->cash->getId(), $r['cashRegister'], $resp);
......@@ -306,9 +302,13 @@ class HttpTicketTest extends TestCase
$this->assertEquals($this->cash->getId(), $s1['cashRegister'], $resp);
$this->assertEquals($this->session->getSequence(), $s1['sequence'], $resp);
$this->assertEquals(1, $s1['number'], $resp);
$snap1 = readTicketSnapshot($this->cash, $s1['sequence'], $s1['number'], $this->dao);
$this->assertTicketModelEqStruct($snap1, $tkt);
$this->assertEquals($this->cash->getId(), $s3['cashRegister'], $resp);
$this->assertEquals($this->session->getSequence(), $s3['sequence'], $resp);
$this->assertEquals(3, $s3['number'], $resp);
$snap3 = readTicketSnapshot($this->cash, $s3['sequence'], $s3['number'], $this->dao);
$this->assertTicketModelEqStruct($snap3, $tkt3);
$this->assertEquals(1, count($data['failures']), $resp);
$r2 = $data['failures'][0];
$this->assertEquals($this->cash->getId(), $r2['cashRegister'], $resp);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment