Skip to content

Commit c7f4928

Browse files
authored
Merge pull request #140 from MadeRelevant/feat-send-invoice-options
Implement feature for additional options when sending a sales invoice
2 parents 06ce3c2 + cd22d32 commit c7f4928

File tree

5 files changed

+398
-8
lines changed

5 files changed

+398
-8
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
composer.phar
22
composer.lock
33
vendor/
4-
.idea
4+
phpunit.xml
5+
.idea

src/Picqer/Financials/Moneybird/Entities/SalesInvoice.php

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
<?php namespace Picqer\Financials\Moneybird\Entities;
22

3+
use InvalidArgumentException;
34
use Picqer\Financials\Moneybird\Actions\Filterable;
45
use Picqer\Financials\Moneybird\Actions\FindAll;
56
use Picqer\Financials\Moneybird\Actions\FindOne;
67
use Picqer\Financials\Moneybird\Actions\PrivateDownloadable;
78
use Picqer\Financials\Moneybird\Actions\Removable;
89
use Picqer\Financials\Moneybird\Actions\Storable;
910
use Picqer\Financials\Moneybird\Actions\Synchronizable;
11+
use Picqer\Financials\Moneybird\Entities\SalesInvoice\SendInvoiceOptions;
1012
use Picqer\Financials\Moneybird\Exceptions\ApiException;
1113
use Picqer\Financials\Moneybird\Model;
1214

@@ -101,20 +103,27 @@ class SalesInvoice extends Model {
101103
/**
102104
* Instruct Moneybird to send the invoice to the contact
103105
*
104-
* @param string $deliveryMethod Email/Post/Manual are allowed types
106+
* @param string|SendInvoiceOptions $deliveryMethodOrOptions
107+
*
105108
* @return $this
106109
* @throws ApiException
107110
*/
108-
public function sendInvoice($deliveryMethod = 'Email')
111+
public function sendInvoice($deliveryMethodOrOptions = SendInvoiceOptions::METHOD_EMAIL)
109112
{
110-
if (!in_array($deliveryMethod, ['Email', 'Post', 'Manual'])) {
111-
throw new ApiException('Invalid delivery method for sending invoice');
113+
if (is_string($deliveryMethodOrOptions)) {
114+
$options = new SendInvoiceOptions($deliveryMethodOrOptions);
115+
} else {
116+
$options = $deliveryMethodOrOptions;
117+
}
118+
unset($deliveryMethodOrOptions);
119+
120+
if (!$options instanceof SendInvoiceOptions) {
121+
$options = is_object($options) ? get_class($options) : gettype($options);
122+
throw new InvalidArgumentException("Expected string or options instance. Received: '$options'");
112123
}
113124

114125
$this->connection->patch($this->endpoint . '/' . $this->id . '/send_invoice', json_encode([
115-
'sales_invoice_sending' => [
116-
'delivery_method' => $deliveryMethod
117-
]
126+
'sales_invoice_sending' => $options->jsonSerialize()
118127
]));
119128

120129
return $this;
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
<?php
2+
namespace Picqer\Financials\Moneybird\Entities\SalesInvoice;
3+
4+
use DateTime;
5+
use InvalidArgumentException;
6+
use JsonSerializable;
7+
8+
/**
9+
* Configuration options when sending an invoice
10+
*
11+
* @package Picqer\Financials\Moneybird\Entities\SalesInvoice
12+
*/
13+
class SendInvoiceOptions implements JsonSerializable {
14+
const METHOD_EMAIL = 'Email';
15+
const METHOD_SIMPLER_INVOICING = 'Simplerinvoicing';
16+
const METHOD_POST = 'Post';
17+
const METHOD_MANUAL = 'Manual';
18+
19+
/** @var string */
20+
private $method;
21+
/** @var string|null */
22+
private $emailAddress;
23+
/** @var string */
24+
private $emailMessage;
25+
26+
/**
27+
* If set to true, the e-mail is scheduled for the given $scheduleDate
28+
* By default it is false meaning the mail is sent immediately
29+
*
30+
* @var bool
31+
*/
32+
private $scheduled = null;
33+
private $scheduleDate;
34+
35+
/** Undocumented boolean properties */
36+
37+
/** @var bool */
38+
private $mergeable;
39+
/** @var bool */
40+
private $deliverUbl;
41+
42+
public function __construct($deliveryMethod = null,
43+
$emailAddress = null, $emailMessage = null) {
44+
$this->setMethod($deliveryMethod ?: self::METHOD_EMAIL);
45+
$this->setEmailAddress($emailAddress);
46+
$this->setEmailMessage($emailMessage);
47+
}
48+
49+
private static function getValidMethods() {
50+
// TODO move this to a private const VALID_METHODS when php 7 is supported
51+
return [
52+
self::METHOD_EMAIL,
53+
self::METHOD_SIMPLER_INVOICING,
54+
self::METHOD_POST,
55+
self::METHOD_MANUAL
56+
];
57+
}
58+
59+
public function schedule(DateTime $date) {
60+
$this->scheduleDate = $date;
61+
$this->scheduled = true;
62+
}
63+
64+
public function isScheduled() {
65+
return $this->scheduled === true;
66+
}
67+
68+
public function jsonSerialize() {
69+
return array_filter([
70+
'delivery_method' => $this->getMethod(),
71+
'sending_scheduled' => $this->isScheduled() ?: null,
72+
'deliver_ubl' => $this->getDeliverUbl(),
73+
'mergeable' => $this->getMergeable(),
74+
'email_address' => $this->getEmailAddress(),
75+
'email_message' => $this->getEmailMessage(),
76+
'invoice_date' => $this->getScheduleDate() ? $this->getScheduleDate()->format('Y-m-d') : null
77+
], function ($item) {
78+
return $item !== null;
79+
});
80+
}
81+
82+
/**
83+
* @return mixed
84+
*/
85+
public function getScheduleDate() {
86+
return $this->scheduleDate;
87+
}
88+
89+
/**
90+
* @return string
91+
*/
92+
public function getMethod() {
93+
return $this->method;
94+
}
95+
96+
/**
97+
* @param string $method
98+
*/
99+
public function setMethod($method) {
100+
$validMethods = self::getValidMethods();
101+
if (!in_array($method, $validMethods)) {
102+
$method = is_object($method) ? get_class($method) : $method;
103+
$validMethodNames = implode(',', $validMethods);
104+
throw new InvalidArgumentException("Invalid method: '$method'. Expected one of: '$validMethodNames'");
105+
}
106+
107+
$this->method = $method;
108+
}
109+
110+
/**
111+
* @return null|string
112+
*/
113+
public function getEmailAddress() {
114+
return $this->emailAddress;
115+
}
116+
117+
/**
118+
* @param null|string $emailAddress
119+
*/
120+
public function setEmailAddress($emailAddress) {
121+
$this->emailAddress = $emailAddress;
122+
}
123+
124+
/**
125+
* @return string
126+
*/
127+
public function getEmailMessage() {
128+
return $this->emailMessage;
129+
}
130+
131+
/**
132+
* @param string $emailMessage
133+
*/
134+
public function setEmailMessage($emailMessage) {
135+
$this->emailMessage = $emailMessage;
136+
}
137+
138+
/**
139+
* @return bool
140+
*/
141+
public function getMergeable() {
142+
return $this->mergeable;
143+
}
144+
145+
/**
146+
* @param bool $mergeable
147+
*/
148+
public function setMergeable($mergeable) {
149+
$this->mergeable = $mergeable;
150+
}
151+
152+
/**
153+
* @return bool
154+
*/
155+
public function getDeliverUbl() {
156+
return $this->deliverUbl;
157+
}
158+
159+
/**
160+
* @param bool $deliverUbl
161+
*/
162+
public function setDeliverUbl($deliverUbl) {
163+
$this->deliverUbl = $deliverUbl;
164+
}
165+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
namespace PicqerTest\Financials\Moneybird\Entities\SalesInvoice;
3+
4+
use DateTime;
5+
use InvalidArgumentException;
6+
use JsonSerializable;
7+
use PHPUnit\Framework\TestCase;
8+
use Picqer\Financials\Moneybird\Entities\SalesInvoice\SendInvoiceOptions;
9+
10+
class SendInvoiceOptionsTest extends TestCase {
11+
12+
private $validMethods = [
13+
SendInvoiceOptions::METHOD_EMAIL,
14+
SendInvoiceOptions::METHOD_SIMPLER_INVOICING,
15+
SendInvoiceOptions::METHOD_POST,
16+
SendInvoiceOptions::METHOD_MANUAL
17+
];
18+
19+
public function testConstructorArguments() {
20+
$options =
21+
new SendInvoiceOptions(SendInvoiceOptions::METHOD_POST, '[email protected]', 'my message');
22+
self::assertEquals(SendInvoiceOptions::METHOD_POST, $options->getMethod());
23+
self::assertEquals('[email protected]', $options->getEmailAddress());
24+
self::assertEquals('my message', $options->getEmailMessage());
25+
}
26+
27+
public function testDefaultMethodIsEmail() {
28+
$options = new SendInvoiceOptions();
29+
self::assertEquals(SendInvoiceOptions::METHOD_EMAIL, $options->getMethod());
30+
}
31+
32+
public function testMethodIsValidated() {
33+
try {
34+
new SendInvoiceOptions('some-invalid-method');
35+
self::fail('Should have thrown exception');
36+
} catch (InvalidArgumentException $e) {
37+
38+
foreach ($this->validMethods as $validMethod) {
39+
self::assertContains($validMethod, $e->getMessage());
40+
}
41+
42+
self::assertContains('some-invalid-method', $e->getMessage(), 'Did not provide which value is invalid');
43+
}
44+
}
45+
46+
47+
public function testIsSerializable() {
48+
$options = new SendInvoiceOptions();
49+
self::assertInstanceOf(JsonSerializable::class, $options);
50+
}
51+
52+
public function testSerializes() {
53+
$options = new SendInvoiceOptions(null, '[email protected]', 'my message');
54+
$options->setDeliverUbl(true);
55+
$options->setMergeable(true);
56+
$options->schedule(new DateTime('2018-01-01'));
57+
58+
$json = $options->jsonSerialize();
59+
self::assertEquals(SendInvoiceOptions::METHOD_EMAIL, $json['delivery_method']);
60+
self::assertEquals(true, $json['sending_scheduled']);
61+
self::assertEquals(true, $json['mergeable']);
62+
self::assertEquals('[email protected]', $json['email_address']);
63+
self::assertEquals('my message', $json['email_message']);
64+
self::assertEquals('2018-01-01', $json['invoice_date']);
65+
}
66+
67+
public function testOmitsNullValues() {
68+
$options = new SendInvoiceOptions(null, '[email protected]');
69+
70+
$json = $options->jsonSerialize();
71+
self::assertEquals(SendInvoiceOptions::METHOD_EMAIL, $json['delivery_method']);
72+
self::assertEquals('[email protected]', $json['email_address']);
73+
74+
$omittedKeys = [
75+
'sending_scheduled', 'email_message', 'invoice_date',
76+
'mergeable'
77+
];
78+
foreach ($omittedKeys as $key) {
79+
self::assertArrayNotHasKey($key, $json);
80+
}
81+
}
82+
83+
public function testGetSetEmailAddress() {
84+
$options = new SendInvoiceOptions();
85+
self::assertNull($options->getEmailAddress());
86+
87+
$options->setEmailAddress('[email protected]');
88+
self::assertEquals('[email protected]', $options->getEmailAddress());
89+
}
90+
91+
public function testGetSetEmailMessage() {
92+
$options = new SendInvoiceOptions();
93+
self::assertNull($options->getEmailAddress());
94+
95+
$options->setEmailMessage('my message');
96+
self::assertEquals('my message', $options->getEmailMessage());
97+
}
98+
99+
public function testGetSetSchedule() {
100+
$options = new SendInvoiceOptions();
101+
self::assertFalse($options->isScheduled());
102+
self::assertNull($options->getScheduleDate());
103+
104+
$date = new DateTime();
105+
$options->schedule($date);
106+
107+
self::assertEquals($date, $options->getScheduleDate());
108+
self::assertTrue($options->isScheduled());
109+
}
110+
111+
public function testGetSetDeliverUbl() {
112+
$options = new SendInvoiceOptions();
113+
self::assertNull($options->getDeliverUbl());
114+
115+
$options->setDeliverUbl(true);
116+
self::assertTrue($options->getDeliverUbl());
117+
}
118+
119+
public function testGetSetMethod() {
120+
$options = new SendInvoiceOptions();
121+
122+
foreach ($this->validMethods as $method) {
123+
$options->setMethod($method);
124+
self::assertEquals($method, $options->getMethod());
125+
}
126+
}
127+
128+
public function testGetSetMergeable() {
129+
$options = new SendInvoiceOptions();
130+
self::assertNull($options->getMergeable());
131+
132+
$options->setMergeable(true);
133+
self::assertTrue($options->getMergeable());
134+
}
135+
}

0 commit comments

Comments
 (0)