Skip to content
This repository was archived by the owner on Apr 29, 2025. It is now read-only.

Commit c68a08f

Browse files
committed
Replaces array with Position class
1 parent 7214c0b commit c68a08f

File tree

5 files changed

+211
-82
lines changed

5 files changed

+211
-82
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ $ composer require mrkriskrisu/rewe-ereceipt-parser
99
```json
1010
{
1111
"require": {
12-
"mrkriskrisu/rewe-ereceipt-parser": "^0.1"
12+
"mrkriskrisu/rewe-ereceipt-parser": "^0.3"
1313
}
1414
}
1515
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace REWEParser\Exception;
4+
5+
class PositionNotFoundException extends \Exception
6+
{
7+
8+
}

src/Position.php

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
namespace REWEParser;
4+
5+
use REWEParser\Exception\ReceiptParseException;
6+
7+
class Position
8+
{
9+
10+
private $name;
11+
private $priceTotal;
12+
private $priceSingle;
13+
private $taxCode;
14+
15+
private $weight;
16+
private $amount;
17+
18+
/**
19+
* The name of the product.
20+
* @return string|NULL
21+
*/
22+
public function getName()
23+
{
24+
return $this->name;
25+
}
26+
27+
/**
28+
* The total sum of the position
29+
* @return float
30+
* @throws ReceiptParseException
31+
*/
32+
public function getPriceTotal()
33+
{
34+
if ($this->priceTotal !== NULL)
35+
return $this->priceTotal;
36+
if ($this->priceSingle !== NULL && $this->amount !== NULL)
37+
return $this->priceSingle * $this->amount;
38+
if ($this->priceSingle !== NULL && $this->weight !== NULL)
39+
return $this->priceSingle * $this->weight;
40+
throw new ReceiptParseException();
41+
}
42+
43+
/**
44+
* The single value for one unit of the product
45+
* @return float
46+
* @throws ReceiptParseException
47+
*/
48+
public function getPriceSingle()
49+
{
50+
if ($this->priceSingle !== NULL)
51+
return $this->priceSingle;
52+
if ($this->priceTotal !== NULL && $this->amount !== NULL)
53+
return $this->priceTotal / $this->amount;
54+
if ($this->priceTotal !== NULL && $this->weight !== NULL)
55+
return $this->priceTotal / $this->weight;
56+
if ($this->priceTotal !== NULL)
57+
return $this->priceTotal;
58+
throw new ReceiptParseException();
59+
}
60+
61+
/**
62+
* The Tax Code of the position (e.g. "A" or "B")
63+
* @return string|NULL
64+
*/
65+
public function getTaxCode()
66+
{
67+
return $this->taxCode;
68+
}
69+
70+
/**
71+
* The weight of the position (if the product is weightable)
72+
* @return float|NULL
73+
*/
74+
public function getWeight()
75+
{
76+
return $this->weight;
77+
}
78+
79+
/**
80+
* The amount of the position (if the product is countable)
81+
* @return int|NULL
82+
*/
83+
public function getAmount()
84+
{
85+
return $this->amount;
86+
}
87+
88+
public function setName(string $name)
89+
{
90+
$this->name = $name;
91+
}
92+
93+
public function setPriceTotal(string $priceTotal)
94+
{
95+
$this->priceTotal = $priceTotal;
96+
}
97+
98+
public function setPriceSingle(string $priceSingle)
99+
{
100+
$this->priceSingle = $priceSingle;
101+
}
102+
103+
public function setTaxCode(string $taxCode)
104+
{
105+
$this->taxCode = $taxCode;
106+
}
107+
108+
public function setWeight(string $weight)
109+
{
110+
$this->weight = $weight;
111+
}
112+
113+
public function setAmount(string $amount)
114+
{
115+
$this->amount = $amount;
116+
}
117+
118+
}

src/Receipt.php

Lines changed: 61 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@
33
namespace REWEParser;
44

55
use Carbon\Carbon;
6+
use REWEParser\Exception\PositionNotFoundException;
67
use REWEParser\Exception\ReceiptParseException;
78

89
class Receipt
910
{
1011
private $raw_receipt;
12+
private $expl_receipt;
1113

1214
function __construct(string $raw_receipt)
1315
{
1416
$this->raw_receipt = $raw_receipt;
17+
$this->expl_receipt = explode("\n", $raw_receipt);
1518
}
1619

1720
/**
@@ -149,6 +152,20 @@ private function getProductEndLine(): int
149152
throw new ReceiptParseException();
150153
}
151154

155+
/**
156+
* @param string $name
157+
* @return Position
158+
* @throws PositionNotFoundException|ReceiptParseException
159+
*/
160+
public function getPositionByName(string $name): Position
161+
{
162+
foreach ($this->getPositions() as $position) {
163+
if ($position->getName() == $name)
164+
return $position;
165+
}
166+
throw new PositionNotFoundException("Position '$name' not found");
167+
}
168+
152169
/**
153170
* @return array
154171
* @throws ReceiptParseException
@@ -157,74 +174,67 @@ public function getPositions(): array
157174
{
158175
$positions = [];
159176

160-
$startLine = $this->getProductStartLine();
161-
$endLine = $this->getProductEndLine();
162-
163177
$rawPos = explode("\n", $this->raw_receipt);
164-
$lastPos = NULL;
178+
$lastPosition = NULL;
165179

166-
for ($lineNr = $startLine; $lineNr <= $endLine; $lineNr++) {
180+
for ($lineNr = $this->getProductStartLine(); $lineNr <= $this->getProductEndLine(); $lineNr++) {
167181
$line = trim($rawPos[$lineNr]);
168182

169-
if (strpos($line, ' Stk x') !== false && $lastPos != NULL) {
183+
if ($this->isProductLine($lineNr)) {
170184

171-
if (preg_match('/(-?\d{1,}) Stk x *(-?\d{1,},\d{2})/', $line, $match)) {
172-
$lastPos['amount'] = (int)$match[1];
173-
$lastPos['price_single'] = (float)str_replace(',', '.', $match[2]);
185+
if ($lastPosition !== NULL) {
186+
$positions[] = $lastPosition;
187+
$lastPosition = NULL;
174188
}
175189

176-
} else if (strpos($line, 'kg') !== false && $lastPos != NULL) {
190+
if (preg_match('/(.*) (-?\d{1,},\d{2}) (.{1})/', $line, $match)) {
191+
$lastPosition = new Position();
192+
$lastPosition->setName(trim($match[1]));
193+
$lastPosition->setPriceTotal((float)str_replace(',', '.', $match[2]));
194+
$lastPosition->setTaxCode($match[3]);
195+
} else throw new ReceiptParseException("Error while parsing Product line");
177196

178-
if (preg_match('/(-?\d{1,},\d{3}) kg x *(-?\d{1,},\d{2}) EUR/', $line, $match)) {
179-
$lastPos['weight'] = (float)str_replace(',', '.', $match[1]);
180-
$lastPos['price_single'] = (float)str_replace(',', '.', $match[2]);
181-
} else if (preg_match('/Handeingabe E-Bon *(-?\d{1,},\d{3}) kg/', $line, $match)) {
182-
$lastPos['weight'] = (float)str_replace(',', '.', $match[1]);
183-
}
197+
} else if ($this->isAmountLine($lineNr)) {
184198

185-
} else {
186-
if ($lastPos != NULL && isset($lastPos['name']) && isset($lastPos['price_total'])) {
187-
if (!isset($lastPos['price_single']) && isset($lastPos['weight']))
188-
$lastPos['price_single'] = $lastPos['price_total'] / $lastPos['weight'];
189-
if (!isset($lastPos['price_single']) && isset($lastPos['amount']))
190-
$lastPos['price_single'] = $lastPos['price_total'] / $lastPos['amount'];
191-
if (!isset($lastPos['price_single'])) {
192-
$lastPos['price_single'] = $lastPos['price_total'];
193-
$lastPos['amount'] = 1;
194-
}
195-
196-
$positions[] = $lastPos;
197-
$lastPos = NULL;
198-
}
199+
if (preg_match('/(-?\d{1,}) Stk x *(-?\d{1,},\d{2})/', $line, $match)) {
200+
$lastPosition->setAmount((int)$match[1]);
201+
$lastPosition->setPriceSingle((float)str_replace(',', '.', $match[2]));
202+
} else throw new ReceiptParseException("Error while parsing Amount line");
199203

204+
} else if ($this->isWeightLine($lineNr)) {
200205

201-
if (preg_match('/(.*) (-?\d{1,},\d{2}) (.{1})/', $line, $match)) {
202-
$lastPos = [
203-
'name' => trim($match[1]),
204-
'price_total' => (float)str_replace(',', '.', $match[2]),
205-
'tax_code' => $match[3]
206-
];
207-
}
206+
if (preg_match('/(-?\d{1,},\d{3}) kg x *(-?\d{1,},\d{2}) EUR/', $line, $match)) {
207+
$lastPosition->setWeight((float)str_replace(',', '.', $match[1]));
208+
$lastPosition->setPriceSingle((float)str_replace(',', '.', $match[2]));
209+
} else if (preg_match('/Handeingabe E-Bon *(-?\d{1,},\d{3}) kg/', $line, $match)) {
210+
$lastPosition->setWeight((float)str_replace(',', '.', $match[1]));
211+
} else throw new ReceiptParseException("Error while parsing Weight line");
208212

209-
}
210-
}
213+
} else throw new ReceiptParseException("Error while parsing unknown receipt line");
211214

212-
if ($lastPos != NULL && isset($lastPos['name']) && isset($lastPos['price_total'])) {
213-
if (!isset($lastPos['price_single']) && isset($lastPos['weight']))
214-
$lastPos['price_single'] = $lastPos['price_total'] / $lastPos['weight'];
215-
if (!isset($lastPos['price_single']) && isset($lastPos['amount']))
216-
$lastPos['price_single'] = $lastPos['price_total'] / $lastPos['amount'];
217-
if (!isset($lastPos['price_single'])) {
218-
$lastPos['price_single'] = $lastPos['price_total'];
219-
$lastPos['amount'] = 1;
220-
}
221-
$positions[] = $lastPos;
222-
$lastPos = NULL;
223215
}
224216

217+
if ($lastPosition !== NULL)
218+
$positions[] = $lastPosition;
219+
225220
if (count($positions) == 0)
226-
throw new ReceiptParseException();
221+
throw new ReceiptParseException("Cannot parse any products on receipt");
227222

228223
return $positions;
229224
}
225+
226+
private function isWeightLine($lineNr)
227+
{
228+
return strpos($this->expl_receipt[$lineNr], 'kg') !== false;
229+
}
230+
231+
private function isAmountLine($lineNr)
232+
{
233+
return strpos($this->expl_receipt[$lineNr], ' Stk x') !== false;
234+
}
235+
236+
private function isProductLine($lineNr)
237+
{
238+
return !$this->isWeightLine($lineNr) && !$this->isAmountLine($lineNr);
239+
}
230240
}

tests/ReceiptParsingTest.php

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public function testNegativeTotalAmount(): void
2323
/**
2424
* @return void
2525
* @throws \REWEParser\Exception\ReceiptParseException
26+
* @throws \REWEParser\Exception\PositionNotFoundException
2627
* @throws \Spatie\PdfToText\Exceptions\PdfNotFound
2728
*/
2829
public function testBonParsingWeight(): void
@@ -37,31 +38,27 @@ public function testBonParsingWeight(): void
3738
$this->assertEquals(5, $receipt->getEarnedPaybackPoints());
3839
$this->assertContains("EC-Cash", $receipt->getPaymentMethods());
3940
$this->assertEquals(1577880000, $receipt->getTimestamp()->getTimestamp());
40-
41-
$positions = [];
42-
foreach ($receipt->getPositions() as $position)
43-
$positions[$position['name']] = $position;
44-
45-
$this->assertEquals(1, $positions['BROT']['price_single']);
46-
$this->assertEquals(0.5, $positions['AUFSCHNITT']['price_single']);
47-
$this->assertEquals(0.5, $positions['NATUR-JOGHURT']['price_single']);
48-
$this->assertEquals(0.01, $positions['ESSEN']['price_single']);
49-
$this->assertEquals(1.99, $positions['BANANE']['price_single']);
50-
$this->assertEquals(2.99, $positions['BANANE']['price_total']);
51-
$this->assertEquals(1.5, $positions['BANANE']['weight']);
52-
$this->assertEquals(1, $positions['EIER']['price_single']);
53-
$this->assertEquals(1, $positions['WEIZENMEHL']['price_single']);
54-
$this->assertEquals(1, $positions['WASSER']['price_single']);
55-
$this->assertEquals(1, $positions['SOFTDRINK']['price_single']);
56-
$this->assertEquals(1, $positions['MILCH']['price_single']);
57-
$this->assertEquals(1, $positions['EIS']['price_single']);
41+
$this->assertEquals(1, $receipt->getPositionByName('BROT')->getPriceSingle());
42+
$this->assertEquals(0.5, $receipt->getPositionByName('AUFSCHNITT')->getPriceSingle());
43+
$this->assertEquals(0.5, $receipt->getPositionByName('NATUR-JOGHURT')->getPriceSingle());
44+
$this->assertEquals(0.01, $receipt->getPositionByName('ESSEN')->getPriceSingle());
45+
$this->assertEquals(1.99, $receipt->getPositionByName('BANANE')->getPriceSingle());
46+
$this->assertEquals(2.99, $receipt->getPositionByName('BANANE')->getPriceTotal());
47+
$this->assertEquals(1.5, $receipt->getPositionByName('BANANE')->getWeight());
48+
$this->assertEquals(1, $receipt->getPositionByName('EIER')->getPriceSingle());
49+
$this->assertEquals(1, $receipt->getPositionByName('WEIZENMEHL')->getPriceSingle());
50+
$this->assertEquals(1, $receipt->getPositionByName('WASSER')->getPriceSingle());
51+
$this->assertEquals(1, $receipt->getPositionByName('SOFTDRINK')->getPriceSingle());
52+
$this->assertEquals(1, $receipt->getPositionByName('MILCH')->getPriceSingle());
53+
$this->assertEquals(1, $receipt->getPositionByName('EIS')->getPriceSingle());
5854

5955
}
6056

6157
/**
6258
* @return void
6359
* @throws \REWEParser\Exception\ReceiptParseException
6460
* @throws \Spatie\PdfToText\Exceptions\PdfNotFound
61+
* @throws \REWEParser\Exception\PositionNotFoundException
6562
*/
6663
public function testBonParsingPaymentMethods(): void
6764
{
@@ -77,18 +74,14 @@ public function testBonParsingPaymentMethods(): void
7774
$this->assertContains("VISA", $receipt->getPaymentMethods());
7875
$this->assertEquals(1577880000, $receipt->getTimestamp()->getTimestamp());
7976

80-
$positions = [];
81-
foreach ($receipt->getPositions() as $position)
82-
$positions[$position['name']] = $position;
83-
84-
$this->assertEquals(0.25, $positions['LEERGUT']['price_single']);
85-
$this->assertEquals(2.99, $positions['KARTOFFELN']['price_single']);
86-
$this->assertEquals(1.49, $positions['NUDELN']['price_single']);
87-
$this->assertEquals(0.49, $positions['QUARK']['price_single']);
88-
$this->assertEquals(1.99, $positions['SÜßIGKEITEN']['price_single']);
89-
$this->assertEquals(0.69, $positions['SCHOKOLADE']['price_single']);
90-
$this->assertEquals(1.38, $positions['SCHOKOLADE']['price_total']);
91-
$this->assertEquals(0.53, $positions['SCHMAND 24%']['price_single']);
77+
$this->assertEquals(0.25, $receipt->getPositionByName('LEERGUT')->getPriceSingle());
78+
$this->assertEquals(2.99, $receipt->getPositionByName('KARTOFFELN')->getPriceSingle());
79+
$this->assertEquals(1.49, $receipt->getPositionByName('NUDELN')->getPriceSingle());
80+
$this->assertEquals(0.49, $receipt->getPositionByName('QUARK')->getPriceSingle());
81+
$this->assertEquals(1.99, $receipt->getPositionByName('SÜßIGKEITEN')->getPriceSingle());
82+
$this->assertEquals(0.69, $receipt->getPositionByName('SCHOKOLADE')->getPriceSingle());
83+
$this->assertEquals(1.38, $receipt->getPositionByName('SCHOKOLADE')->getPriceTotal());
84+
$this->assertEquals(0.53, $receipt->getPositionByName('SCHMAND 24%')->getPriceSingle());
9285
}
9386

9487
/**

0 commit comments

Comments
 (0)