protected function setUp() { // Products $taxCat = new TaxCat("Tax"); $tax = new Tax(null, "Tax", stdtimefstr("2001-01-01 00:00:00"), 0.1); $taxCat->addTax($tax); $taxCat->id = TaxesService::createCat($taxCat); $cat = new Category(null, "Category", false, 1); $cat->id = CategoriesService::createCat($cat); $prd1 = new Product("REF", "product", 1.0, $cat->id, null, 1, $taxCat->id, true, true, 0.3, null, "12345", false, true, 0.2); $prd1->id = ProductsService::create($prd1); $prd2 = new Product("REF2", "product2", 2.0, $cat->id, null, 3, $taxCat->id, true, false); $prd2->id = ProductsService::create($prd2); $this->products = array($prd1, $prd2); // Locations $locSrv = new LocationsService(); $loc1 = new Location("Location1"); $loc1->id = $locSrv->create($loc1); $loc2 = new Location("Location2"); $loc2->id = $locSrv->create($loc2); $this->locations = array($loc1, $loc2); // Stocks $lvl11 = new StockLevel($prd1->id, $loc1->id, null, null, 10); $lvl11->id = StocksService::createLevel($lvl11); $lvl12 = new StockLevel($prd1->id, $loc2->id, null, 5.2, null); $lvl12->id = StocksService::createLevel($lvl12); $lvl21 = new StockLevel($prd2->id, $loc1->id, null, 7, 20); $lvl21->id = StocksService::createLevel($lvl21); $move = new StockMove(stdtimefstr("2014-01-01 00:00:00"), StockMove::REASON_IN_BUY, $loc1->id, $prd1->id, null, 3.5, 2.1); StocksService::addMove($move); $this->levels = array($lvl11, $lvl12, $lvl21); }
protected function proceed() { switch ($this->action) { case 'getAll': $this->succeed(StocksService::getLevels($this->params['locationId'])); break; } }
static function delete($id) { $pdo = PDOBuilder::getPDO(); $db = DB::get(); $ticket = TicketsService::get($id); if ($ticket === null) { return false; } $cashSrv = new CashesService(); $cash = $cashSrv->get($ticket->cashId); if ($cash === null || $cash->isClosed()) { return false; } $cashRegSrv = new CashRegistersService(); $cashReg = $cashRegSrv->getFromCashId($cash->id); // As cash must be opened, cashregister location is considered accurate $locationId = $cashReg->locationId; $newTransaction = !$pdo->inTransaction(); if ($newTransaction) { $pdo->beginTransaction(); } // Delete ticket lines // Also check for prepayments refill $stmtLines = $pdo->prepare("DELETE FROM TICKETLINES " . "WHERE TICKET = :id"); $stmtLines->bindParam(":id", $ticket->id); foreach ($ticket->lines as $line) { // Update stock if ($line->productId !== null) { $discountRate = $ticket->discountRate; $fullDiscount = $discountRate + $line->discountRate; $discountPrice = $line->price * (1.0 - $fullDiscount); $move = new StockMove($ticket->date, StockMove::REASON_IN_REFUND, $line->productId, $locationId, $line->attrSetInstId, $line->quantity, $discountPrice); if (StocksService::addMove($move) === false) { if ($newTransaction) { $pdo->rollback(); } return false; } } // Check prepayment refill // Refill is not affected by discount $prepaidIds = ProductsService::getPrepaidIds(); if ($ticket->customerId !== null && in_array($line->productId, $prepaidIds)) { $custSrv = new CustomersService(); $ok = $custSrv->addPrepaid($ticket->customerId, -$line->price * $line->quantity); if ($ok === false) { if ($newTransaction) { $pdo->rollback(); } return false; } } } if ($stmtLines->execute() === false) { if ($newTransaction) { $pdo->rollback(); } return false; } // Delete payments // Also check for prepayment debit and debt $stmtPay = $pdo->prepare("DELETE FROM PAYMENTS WHERE RECEIPT = :id"); $stmtPay->bindParam(":id", $ticket->id); foreach ($ticket->payments as $payment) { if ($payment->type == 'prepaid' || $payment->type == 'debt') { $custSrv = new CustomersService(); if ($payment->type == 'prepaid') { $ok = $custSrv->addPrepaid($ticket->customerId, $payment->amount); } else { $ok = $custSrv->recoverDebt($ticket->customerId, $payment->amount); } if ($ok === false) { if ($newTransaction) { $pdo->rollback(); } return false; } } } if ($stmtPay->execute() === false) { if ($newTransaction) { $pdo->rollback(); } return false; } // Delete taxlines $stmtTax = $pdo->prepare("DELETE FROM TAXLINES WHERE RECEIPT = :id"); $stmtTax->bindParam(":id", $ticket->id); if ($stmtTax->execute() === false) { if ($newTransaction) { $pdo->rollback(); } return false; } // Delete ticket $discountRate = $ticket->discountRate; $stmtTkt = $pdo->prepare("DELETE FROM TICKETS WHERE ID = :id"); $stmtTkt->bindParam(':id', $ticket->id); if ($stmtTkt->execute() === false) { if ($newTransaction) { $pdo->rollback(); } return false; } // Delete receipt $stmtRcpt = $pdo->prepare("DELETE FROM RECEIPTS WHERE ID = :id"); $stmtRcpt->bindParam(":id", $ticket->id); if ($stmtRcpt->execute() === false) { if ($newTransaction) { $pdo->rollback(); } return false; } if ($newTransaction) { $pdo->commit(); } return true; }
/** @depends testCreateMoveBuy * @depends testReadLevel */ public function testReadQty() { $level = new StockLevel($this->products[0]->id, $this->locations[0]->id, null, 1.2, 15.6, 3.5); // Quantity is set matching to move $level->id = StocksService::createLevel($level); $move = new StockMove(stdtimefstr("2014-01-01 00:00:00"), StockMove::REASON_IN_BUY, $level->productId, $level->locationId, $level->attrSetInstId, $level->qty, 10); $move->id = StocksService::addMove($move); $read = StocksService::getLevel($move->productId, $move->locationId, $move->attrSetInstId); $this->checkEquality($read, $level); }
/** @depends testSaveLine */ function testSaveMultiLines() { $date = stdtimefstr("2013-01-01 00:00:00"); $attrSetInst = new AttributeSetInstance($this->attrSet->id, "Value"); $attrsId = TicketsService::createAttrSetInst($attrSetInst); $line1 = new TicketLine(1, $this->prd, $attrSetInst->id, 1, 12, $this->tax); $line2 = new TicketLine(2, $this->prd2, null, 2, 10, $this->tax2); $line3 = new TicketLine(3, $this->prd, null, 1.5, 10, $this->tax); $payment1 = new Payment("cash", 12, $this->currency->id, 14); $payment2 = new Payment("cheque", 25, $this->currency->id, 20); $ticket = new Ticket(Ticket::TYPE_SELL, $this->user->id, $date, array($line1, $line2, $line3), array($payment1, $payment2), $this->cash->id, null, null); $id = TicketsService::save($ticket, $this->location->id); $pdo = PDOBuilder::getPDO(); $db = DB::get(); // Check sale lines $stmtLines = $pdo->prepare("SELECT * FROM TICKETLINES"); $this->assertNotEquals($stmtLines->execute(), false, "Ticket query failed"); $toCheck = array($line1, $line2, $line3); $count = 0; while ($row = $stmtLines->fetch()) { $ref = null; $count++; if ($row['LINE'] == 1) { $ref = $line1; } else { if ($row['LINE'] == 2) { $ref = $line2; } else { if ($row['LINE'] == 3) { $ref = $line3; } } } $this->assertNotNull($ref, "Unknown line"); $this->checkSaleEquality($id, $ref, $row); for ($i = 0; $i < count($toCheck); $i++) { $l = $toCheck[$i]; if ($l->dispOrder == $ref->dispOrder) { array_splice($toCheck, $i, 1); break; } } } $this->assertEquals(3, $count, "Sale line count mismatch"); $this->assertEquals(0, count($toCheck), "Duplicated sale lines"); // Check tax lines $stmtTax = $pdo->prepare("SELECT * FROM TAXLINES"); $this->assertNotEquals($stmtTax->execute(), false, "Tax lines query failed"); $toCheck = array($this->tax->id, $this->tax2->id); for ($i = 0; $i < 2; $i++) { $row = $stmtTax->fetch(); $this->assertNotEquals(false, $row, "Not enough tax line found"); $ref = null; if ($row['TAXID'] == $this->tax->id) { $ref = $this->tax->id; $this->checkTaxEquality($id, $ref, 27, 2.7, $row); } else { if ($row['TAXID'] == $this->tax2->id) { $ref = $this->tax2->id; $this->checkTaxEquality($id, $ref, 20, 4, $row); } } $this->assertNotNull($ref, "Unknown tax line"); foreach ($toCheck as $j => $taxId) { if ($ref == $taxId) { array_splice($toCheck, $j, 1); break; } } } $this->assertEquals(0, count($toCheck), "Duplicated tax lines"); $row = $stmtTax->fetch(); $this->assertFalse($row, "Too much tax lines found"); // Check payment lines $toCheck = array($payment1, $payment2); $stmtPmt = $pdo->prepare("SELECT * FROM PAYMENTS"); $this->assertNotEquals($stmtPmt->execute(), false, "Payment lines query failed"); $count = 0; while ($row = $stmtPmt->fetch()) { $ref = null; $count++; if ($row['PAYMENT'] == "cash") { $ref = $payment1; } else { if ($row['PAYMENT'] == "cheque") { $ref = $payment2; } } $this->assertNotNull($ref, "Unknown line"); $this->checkPaymentEquality($id, $ref, $row); foreach ($toCheck as $i => $pmt) { if ($pmt->type == $ref->type) { array_splice($toCheck, $i, 1); break; } } } $this->assertEquals(2, $count, "Payment line count mismatch"); $this->assertEquals(0, count($toCheck), "Duplicated payment lines"); // Check stocks $level = StocksService::getLevel($this->prd->id, $this->location->id, null); $this->assertEquals(-1.5, $level->qty); $level = StocksService::getLevel($this->prd2->id, $this->location->id, null); $this->assertEquals(-2, $level->qty); $this->markTestIncomplete("Check stock level with attribute"); }
/** Create an inventory. If date, missingQty or unitValue is null they are * computed from current stock and current date. */ public function create($inventory) { $pdo = PDOBuilder::getPDO(); $db = DB::get(); $newTransaction = !$pdo->inTransaction(); if ($newTransaction) { $pdo->beginTransaction(); } if ($inventory->date === null) { // Set date to now $inventory->date = time(); } // Insert inventory $invStmt = $pdo->prepare("INSERT INTO STOCK_INVENTORY (DATE, " . "LOCATION_ID) VALUES (:date, :locId)"); $invStmt->bindParam(":date", $db->dateVal($inventory->date)); $invStmt->bindParam(":locId", $inventory->locationId); if ($invStmt->execute() !== false) { $id = $pdo->lastInsertId(static::$dbTable . "_" . static::$dbIdField . "_seq"); } else { if ($newTransaction) { $pdo->rollback(); } return false; } // Parse and insert items $stmt = $pdo->prepare("INSERT INTO STOCK_INVENTORYITEM (INVENTORY_ID, " . "PRODUCT_ID, ATTRSETINST_ID, QTY, LOSTQTY, DEFECTQTY, " . "MISSINGQTY, UNITVALUE) VALUES " . "(:id, :prdId, :attrId, :qty, :lostQty, :defectQty, " . ":missingQty, :unitValue)"); $stmt->bindParam(":id", $id); foreach ($inventory->items as $item) { if ($item->missingQty === null) { // Check for missing count on current stock $lvl = StocksService::getLevel($item->productId, $inventory->locationId, $item->attrSetInstId); if ($lvl != null) { $qty = $lvl->qty; $invQty = $item->qty + $item->lostQty + $item->defectQty; $item->missingQty = $qty - $invQty; } else { $item->missingQty = 0; } } if ($item->unitValue === null) { // Compute average value $sql = "SELECT REASON, UNITS, PRICE FROM STOCKDIARY " . "WHERE LOCATION = :loc AND PRODUCT = :prd"; if ($item->attrSetInstId !== null) { $sql .= " AND ATTRIBUTESETINSTANCE_ID = :attr"; } else { $sql .= " AND ATTRIBUTESETINSTANCE_ID IS NULL"; } $sql .= " ORDER BY DATENEW DESC"; $stmtVal = $pdo->prepare($sql); $stmtVal->bindParam(":loc", $item->locationId); $stmtVal->bindParam(":prd", $item->productId); if ($item->attrSetInstId !== null) { $stmtVal->bindParam(":attr", $item->attrSetInstId); } $stmtVal->execute(); $units = 0; $expectedUnits = $item->getTotalQty(); $price = 0.0; while ($row = $stmtVal->fetch() && $units != $expectedUnits) { $units += $row['UNITS']; if ($row['UNITS'] > 0) { $price += $row['PRICE']; } else { $price -= $row['PRICE']; } } if ($units != 0) { $item->unitValue = $price / $units; } else { $item->unitValue = 0; } } // Insert $stmt->bindParam(":prdId", $item->productId); $stmt->bindParam(":attrId", $item->attrSetInstId); $stmt->bindParam(":qty", $item->qty); $stmt->bindParam(":lostQty", $item->lostQty); $stmt->bindParam(":defectQty", $item->defectQty); $stmt->bindParam(":missingQty", $item->missingQty); $stmt->bindParam(":unitValue", $item->unitValue); if ($stmt->execute() === false) { var_dump($stmt->errorInfo()); if ($newTransaction) { $pdo->rollback(); } return false; } } if ($newTransaction) { $pdo->commit(); } return $id; }
/** @depends testCreateFull */ public function testCreateGuessMissing() { $move = new StockMove(stdtimefstr("2014-01-01 00:00:00"), StockMove::REASON_IN_BUY, $this->products[0]->id, $this->locations[0]->id, null, 10, 1); $move->id = StocksService::addMove($move); $item = new InventoryItem(null, $this->products[0]->id, null, 1, 2, 3, null, 5); $inv = new Inventory(stdtimefstr("2001-01-01 00:00:00"), $this->locations[0]->id); $inv->addItem($item); $srv = new InventoriesService(); $id = $srv->create($inv); $this->assertNotEquals(false, $id, "Creation failed"); $pdo = PDOBuilder::getPDO(); $stmt = $pdo->prepare("SELECT * FROM STOCK_INVENTORYITEM"); $this->assertNotEquals(false, $stmt->execute(), "Query failed"); if ($row = $stmt->fetch()) { $this->assertEquals(4, $row["MISSINGQTY"]); $this->markTestIncomplete("Check unit value"); } else { $this->assertTrue(false, "No inventory item found after creation"); } }
static function getLevels($locationId) { $pdo = PDOBuilder::getPDO(); $lvls = array(); /* Start from LOCATIONS table to return a line with null everywhere * if there is no stocklevel and no line at all * if the location does not exist. */ // Get security and max levels $sqlLvl = "SELECT STOCKLEVEL.ID, PRODUCT, STOCKSECURITY, STOCKMAXIMUM " . "FROM LOCATIONS " . "LEFT JOIN STOCKLEVEL ON STOCKLEVEL.LOCATION = LOCATIONS.ID " . "WHERE LOCATIONS.ID = :loc"; $stmtLvl = $pdo->prepare($sqlLvl); $stmtLvl->bindParam(":loc", $locationId); $stmtLvl->execute(); $locationExists = false; while ($row = $stmtLvl->fetch()) { $locationExists = true; if ($row['PRODUCT'] !== null) { $lvls[$row['PRODUCT']] = array($row['ID'], $row['STOCKSECURITY'], $row['STOCKMAXIMUM']); } } if (!$locationExists) { return null; } // Get quantities $qties = array(); $sqlQty = "SELECT PRODUCT, ATTRIBUTESETINSTANCE_ID AS ATTR, UNITS " . "FROM STOCKCURRENT " . "WHERE LOCATION = :loc"; $stmtQty = $pdo->prepare($sqlQty); $stmtQty->bindParam(':loc', $locationId); $stmtQty->execute(); while ($row = $stmtQty->fetch()) { $prdId = $row['PRODUCT']; if (!isset($qties[$prdId])) { $qties[$prdId] = array(); } $qties[$prdId][$row['ATTR']] = $row['UNITS']; } // Merge both ids $prdIds = array(); foreach (array_keys($lvls) as $id) { $prdIds[] = $id; } foreach (array_keys($qties) as $id) { if (!in_array($id, $prdIds)) { $prdIds[] = $id; } } // Merge all data $levels = array(); foreach ($prdIds as $id) { $row = array("PRODUCT" => $id, "LOCATION" => $locationId); if (isset($lvls[$id])) { $row['ID'] = $lvls[$id][0]; $row['STOCKSECURITY'] = $lvls[$id][1]; $row['STOCKMAXIMUM'] = $lvls[$id][2]; } else { $row['ID'] = null; $row['STOCKSECURITY'] = null; $row['STOCKMAXIMUM'] = null; } if (isset($qties[$id])) { foreach ($qties[$id] as $attr => $qty) { $row['ATTRIBUTESETINSTANCE_ID'] = $attr; $row['UNITS'] = $qty; $levels[] = StocksService::buildDBLevel($row); } } else { $row['ATTRIBUTESETINSTANCE_ID'] = null; $row['UNITS'] = null; $levels[] = StocksService::buildDBLevel($row); } } return $levels; }