diff --git a/Library/ExternalInvoice.cs b/Library/ExternalInvoice.cs
index ed28a1b4..cd5d80e7 100644
--- a/Library/ExternalInvoice.cs
+++ b/Library/ExternalInvoice.cs
@@ -34,6 +34,15 @@ public ExternalSubscription ExternalSubscription
internal set { _externalSubscription = value; }
}
+ private string _externalPaymentPhaseUuid;
+ public string ExternalPaymentPhaseUuid => _externalPaymentPhaseUuid;
+
+ private ExternalPaymentPhase _externalPaymentPhase;
+ public ExternalPaymentPhase ExternalPaymentPhase
+ {
+ get { return _externalPaymentPhase ?? (_externalPaymentPhase = ExternalPaymentPhases.Get(_externalPaymentPhaseUuid)); }
+ internal set { _externalPaymentPhase = value; }
+ }
public string ExternalId { get; private set; }
public ExternalInvoiceState State { get; private set; }
public decimal Total { get; private set; }
@@ -86,6 +95,12 @@ internal override void ReadXml(XmlTextReader reader)
_externalSubscriptionUuid = Uri.UnescapeDataString(href.Substring(href.LastIndexOf("/") + 1));
break;
+ case "external_payment_phase":
+ href = reader.GetAttribute("href");
+ if (null != href)
+ _externalPaymentPhaseUuid = Uri.UnescapeDataString(href.Substring(href.LastIndexOf("/") + 1));
+ break;
+
case "external_id":
ExternalId = reader.ReadElementContentAsString();
break;
diff --git a/Library/ExternalPaymentPhase.cs b/Library/ExternalPaymentPhase.cs
new file mode 100644
index 00000000..2b55ec2c
--- /dev/null
+++ b/Library/ExternalPaymentPhase.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Net;
+using System.Xml;
+
+namespace Recurly
+{
+ public class ExternalPaymentPhase : RecurlyEntity
+ {
+ public DateTime StartedAt { get; private set; }
+ public DateTime EndsAt { get; private set; }
+ public int StartingBillingPeriodIndex { get; private set; }
+ public int EndingBillingPeriodIndex { get; private set; }
+ public string OfferType { get; private set; }
+ public string OfferName { get; private set; }
+ public int PeriodCount { get; private set; }
+ public string PeriodLength { get; private set; }
+ public decimal Amount { get; private set; }
+ public string Currency { get; private set; }
+ public DateTime CreatedAt { get; private set; }
+ public DateTime UpdatedAt { get; private set; }
+
+ internal const string UrlPrefix = "/external_payment_phases/";
+
+ internal ExternalPaymentPhase()
+ {
+ }
+ internal ExternalPaymentPhase(XmlTextReader reader)
+ {
+ ReadXml(reader);
+ }
+ #region Read XML documents
+
+ internal override void ReadXml(XmlTextReader reader)
+ {
+ while (reader.Read())
+ {
+ DateTime dateVal;
+
+ if (reader.Name == "external_payment_phase" && reader.NodeType == XmlNodeType.EndElement)
+ break;
+
+ if (reader.NodeType != XmlNodeType.Element) continue;
+
+ switch (reader.Name)
+ {
+ case "started_at":
+ if (DateTime.TryParse(reader.ReadElementContentAsString(), out dateVal))
+ StartedAt = dateVal; ;
+ break;
+
+ case "ends_at":
+ if (DateTime.TryParse(reader.ReadElementContentAsString(), out dateVal))
+ EndsAt = dateVal; ;
+ break;
+
+ case "starting_billing_period_index":
+ StartingBillingPeriodIndex = reader.ReadElementContentAsInt();
+ break;
+
+ case "ending_billing_period_index":
+ EndingBillingPeriodIndex = reader.ReadElementContentAsInt();
+ break;
+
+ case "offer_type":
+ OfferType = reader.ReadElementContentAsString();
+ break;
+
+ case "offer_name":
+ OfferName = reader.ReadElementContentAsString();
+ break;
+
+ case "period_count":
+ PeriodCount = reader.ReadElementContentAsInt();
+ break;
+
+ case "period_length":
+ PeriodLength = reader.ReadElementContentAsString();
+ break;
+
+ case "amount":
+ Amount = reader.ReadElementContentAsDecimal();
+ break;
+
+ case "currency":
+ Currency = reader.ReadElementContentAsString();
+ break;
+
+ case "updated_at":
+ if (DateTime.TryParse(reader.ReadElementContentAsString(), out dateVal))
+ UpdatedAt = dateVal; ;
+ break;
+
+ case "created_at":
+ if (DateTime.TryParse(reader.ReadElementContentAsString(), out dateVal))
+ CreatedAt = dateVal; ;
+ break;
+
+ }
+ }
+ }
+
+ internal override void WriteXml(XmlTextWriter writer)
+ {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region Object Overrides
+
+ public override string ToString()
+ {
+ return "Recurly External Payment Phase: " + StartedAt + " " + EndsAt + " " + StartingBillingPeriodIndex + " " + EndingBillingPeriodIndex + " " + OfferType + " " + OfferName + " " + PeriodCount + " " + PeriodLength + " " + Amount + " " + Currency + " " + CreatedAt + " " + UpdatedAt;
+ }
+
+ #endregion
+ }
+
+ public sealed class ExternalPaymentPhases
+ {
+ ///
+ /// Returns a list of recurly external_payment_phases
+ ///
+ ///
+ public static RecurlyList List()
+ {
+ return List(null);
+ }
+ ///
+ /// Returns a list of recurly external_payment_phases
+ ///
+ /// FilterCriteria used to apply server side sorting and filtering
+ ///
+ public static RecurlyList List(FilterCriteria filter)
+ {
+ filter = filter ?? FilterCriteria.Instance;
+ var parameters = filter.ToNamedValueCollection();
+ return new ExternalPaymentPhaseList(ExternalPaymentPhase.UrlPrefix + "?" + parameters.ToString());
+ }
+ public static ExternalPaymentPhase Get(string uuid)
+ {
+ if (string.IsNullOrWhiteSpace(uuid))
+ {
+ return null;
+ }
+ var externalPaymentPhase = new ExternalPaymentPhase();
+ var statusCode = Client.Instance.PerformRequest(Client.HttpRequestMethod.Get,
+ ExternalPaymentPhase.UrlPrefix + Uri.EscapeDataString(uuid),
+ externalPaymentPhase.ReadXml);
+
+ return statusCode == HttpStatusCode.NotFound ? null : externalPaymentPhase;
+ }
+ }
+}
diff --git a/Library/ExternalSubscription.cs b/Library/ExternalSubscription.cs
index 661b81e3..1e1082bb 100644
--- a/Library/ExternalSubscription.cs
+++ b/Library/ExternalSubscription.cs
@@ -21,6 +21,12 @@ public Account Account
internal set { _account = value; }
}
+ private List _externalInvoices;
+ public List ExternalInvoices
+ {
+ get { return _externalInvoices ?? (_externalInvoices = new List()); }
+ set { _externalInvoices = value; }
+ }
public string ExternalId { get; set; }
public string AppIdentifier { get; private set; }
public string State { get; set; }
@@ -204,5 +210,33 @@ public static RecurlyList GetExternalInvoices(string uuid)
{
return new ExternalInvoiceList(ExternalSubscription.UrlPrefix + Uri.EscapeDataString(uuid) + "/external_invoices/");
}
+
+ ///
+ /// Returns a list of external_payment_phases for this external subscription
+ ///
+ ///
+ public static RecurlyList GetExternalPaymentPhases(string uuid)
+ {
+ return new ExternalPaymentPhaseList(ExternalSubscription.UrlPrefix + Uri.EscapeDataString(uuid) + ExternalPaymentPhase.UrlPrefix);
+ }
+
+ ///
+ /// Returns an external payment phase for external subscription
+ ///
+ ///
+ public static ExternalPaymentPhase GetExternalPaymentPhase(string uuid, string externalPaymentPhaseUuid)
+ {
+ if (string.IsNullOrWhiteSpace(uuid) || string.IsNullOrWhiteSpace(externalPaymentPhaseUuid))
+ {
+ return null;
+ }
+ var externalPaymentPhase = new ExternalPaymentPhase();
+ var statusCode = Client.Instance.PerformRequest(Client.HttpRequestMethod.Get,
+ ExternalSubscription.UrlPrefix + Uri.EscapeDataString(uuid) + ExternalPaymentPhase.UrlPrefix + Uri.EscapeDataString(externalPaymentPhaseUuid),
+ externalPaymentPhase.ReadXml);
+
+ return statusCode == HttpStatusCode.NotFound ? null : externalPaymentPhase;
+ }
+
}
}
diff --git a/Library/List/ExternalInvoiceList.cs b/Library/List/ExternalInvoiceList.cs
index c8998658..ff59b92f 100644
--- a/Library/List/ExternalInvoiceList.cs
+++ b/Library/List/ExternalInvoiceList.cs
@@ -8,6 +8,9 @@ internal ExternalInvoiceList(string baseUrl) : base(Client.HttpRequestMethod.Get
{
}
+ internal ExternalInvoiceList()
+ {
+ }
public override RecurlyList Start
{
get { return HasStartPage() ? new ExternalInvoiceList(StartUrl) : RecurlyList.Empty(); }
diff --git a/Library/List/ExternalPaymentPhaseList.cs b/Library/List/ExternalPaymentPhaseList.cs
new file mode 100644
index 00000000..7a652532
--- /dev/null
+++ b/Library/List/ExternalPaymentPhaseList.cs
@@ -0,0 +1,43 @@
+using System.Xml;
+
+namespace Recurly
+{
+ public class ExternalPaymentPhaseList : RecurlyList
+ {
+ internal ExternalPaymentPhaseList(string baseUrl) : base(Client.HttpRequestMethod.Get, baseUrl)
+ {
+ }
+
+ internal ExternalPaymentPhaseList()
+ {
+ }
+ public override RecurlyList Start
+ {
+ get { return HasStartPage() ? new ExternalPaymentPhaseList(StartUrl) : RecurlyList.Empty(); }
+ }
+
+ public override RecurlyList Next
+ {
+ get { return HasNextPage() ? new ExternalPaymentPhaseList(NextUrl) : RecurlyList.Empty(); }
+ }
+
+ public override RecurlyList Prev
+ {
+ get { return HasPrevPage() ? new ExternalPaymentPhaseList(PrevUrl) : RecurlyList.Empty(); }
+ }
+
+ internal override void ReadXml(XmlTextReader reader)
+ {
+ while (reader.Read())
+ {
+ if (reader.Name == "external_payment_phases" && reader.NodeType == XmlNodeType.EndElement)
+ break;
+
+ if (reader.NodeType == XmlNodeType.Element && reader.Name == "external_payment_phase")
+ {
+ Add(new ExternalPaymentPhase(reader));
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Test/ExternalInvoiceTest.cs b/Test/ExternalInvoiceTest.cs
index e0fca1dd..583fb004 100644
--- a/Test/ExternalInvoiceTest.cs
+++ b/Test/ExternalInvoiceTest.cs
@@ -1,7 +1,7 @@
using System;
-using System.Collections.Generic;
+using System.Xml;
using FluentAssertions;
-using Xunit;
+using Recurly.Test.Fixtures;
namespace Recurly.Test
{
@@ -10,12 +10,16 @@ public class ExternalInvoiceTest : BaseTest
[RecurlyFact(TestEnvironment.Type.Integration)]
public void LookupExternalInvoice()
{
- var uuid = "sl7984v66da8";
- var externalInvoice = ExternalInvoices.Get(uuid);
- externalInvoice.ExternalId.Should().Be("external_id");
+ var xmlFixture = FixtureImporter.Get(FixtureType.ExternalInvoices, "show-200").Xml;
+ XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(xmlFixture));
+ var externalInvoice = new ExternalInvoice();
+ externalInvoice.ReadXml(reader);
+ externalInvoice.ExternalId.Should().Be("2000000458276005");
externalInvoice.State.Should().Be(ExternalInvoice.ExternalInvoiceState.Paid);
- externalInvoice.Total.Should().Be(123);
+ externalInvoice.Total.Should().Be(new decimal(10.45));
externalInvoice.Currency.Should().Be("USD");
+ externalInvoice.ExternalSubscriptionUuid.Should().Be("tsfnx2vn5wh6");
+ externalInvoice.ExternalPaymentPhaseUuid.Should().Be("twqswp627ri3");
externalInvoice.PurchasedAt.Should().NotBe(default(DateTime));
externalInvoice.CreatedAt.Should().NotBe(default(DateTime));
externalInvoice.UpdatedAt.Should().NotBe(default(DateTime));
diff --git a/Test/ExternalPaymentPhaseTest.cs b/Test/ExternalPaymentPhaseTest.cs
new file mode 100644
index 00000000..4091d766
--- /dev/null
+++ b/Test/ExternalPaymentPhaseTest.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Xml;
+using FluentAssertions;
+using Recurly.Test.Fixtures;
+
+namespace Recurly.Test
+{
+ public class ExternalPaymentPhaseTest : BaseTest
+ {
+ [RecurlyFact(TestEnvironment.Type.Integration)]
+ public void LookupExternalPaymentPhase()
+ {
+ var externalPaymentPhase = new ExternalPaymentPhase();
+ var xmlFixture = FixtureImporter.Get(FixtureType.ExternalPaymentPhases, "show-200").Xml;
+ XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(xmlFixture));
+ externalPaymentPhase.ReadXml(reader);
+ externalPaymentPhase.StartedAt.Should().Be(new DateTime(2023, 11, 15, 0, 0, 0, DateTimeKind.Utc));
+ externalPaymentPhase.EndsAt.Should().Be(new DateTime(2023, 11, 17, 21, 27, 10, DateTimeKind.Utc));
+ externalPaymentPhase.StartingBillingPeriodIndex.Should().Be(1);
+ externalPaymentPhase.EndingBillingPeriodIndex.Should().Be(2);
+ externalPaymentPhase.OfferType.Should().Be("FREE_TRIAL");
+ externalPaymentPhase.OfferName.Should().Be("introductory");
+ externalPaymentPhase.PeriodCount.Should().Be(2);
+ externalPaymentPhase.PeriodLength.Should().Be("TWO WEEKS");
+ externalPaymentPhase.Amount.Should().Be(new decimal(1.99));
+ externalPaymentPhase.Currency.Should().Be("USD");
+ externalPaymentPhase.CreatedAt.Should().Be(new DateTime(2023, 11, 15, 16, 16, 43, DateTimeKind.Utc));
+ externalPaymentPhase.UpdatedAt.Should().Be(new DateTime(2023, 11, 15, 16, 16, 43, DateTimeKind.Utc));
+ }
+ }
+}
diff --git a/Test/Fixtures/FixtureImporter.cs b/Test/Fixtures/FixtureImporter.cs
index bb4a64ae..b01b30db 100644
--- a/Test/Fixtures/FixtureImporter.cs
+++ b/Test/Fixtures/FixtureImporter.cs
@@ -8,6 +8,15 @@ namespace Recurly.Test.Fixtures
{
public class FixtureImporter
{
+ public static string GetFixtureTypeDescription(FixtureType type)
+ {
+ DescriptionAttribute[] attributes = (DescriptionAttribute[])type
+ .GetType()
+ .GetField(type.ToString())
+ .GetCustomAttributes(typeof(DescriptionAttribute), false);
+
+ return attributes.Length > 0 ? attributes[0].Description : string.Empty;
+ }
public static FixtureResponse Get(FixtureType type, string name)
{
if (name.IsNullOrEmpty())
@@ -16,7 +25,7 @@ public static FixtureResponse Get(FixtureType type, string name)
if (!Enum.IsDefined(typeof(FixtureType), type))
throw new InvalidEnumArgumentException("type", (int)type, typeof(FixtureType));
- var fixturePath = string.Format("fixtures/{0}/{1}.xml", type, name);
+ var fixturePath = string.Format("Fixtures/{0}/{1}.xml", GetFixtureTypeDescription(type), name);
if (!File.Exists(fixturePath))
throw new FileNotFoundException("Could not locate the fixture response file!", fixturePath);
@@ -66,6 +75,10 @@ private static KeyValuePair ParseHeader(string line)
public enum FixtureType
{
- Accounts
+ Accounts,
+ [Description("external_payment_phases")]
+ ExternalPaymentPhases,
+ [Description("external_invoices")]
+ ExternalInvoices,
}
}
diff --git a/Test/Fixtures/external_invoices/index-200.xml b/Test/Fixtures/external_invoices/index-200.xml
new file mode 100644
index 00000000..36376e10
--- /dev/null
+++ b/Test/Fixtures/external_invoices/index-200.xml
@@ -0,0 +1,37 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml; charset=utf-8
+
+
+
+
+
+
+
+ 2000000458276005
+ paid
+ USD
+ 10.45
+
+
+
+
+
+ twqswp9p3krc
+ random-ass-test-2
+ apple_app_store
+ 2023-11-15T16:16:43Z
+ 2023-11-15T16:16:43Z
+
+ the best banana in the world
+ 10.45
+ USD
+ 1
+ 2023-11-15T16:16:43Z
+ 2023-11-15T16:16:43Z
+
+
+ 2023-11-14T14:15:22Z
+ 2023-11-15T16:16:43Z
+ 2023-11-15T16:16:43Z
+
+
\ No newline at end of file
diff --git a/Test/Fixtures/external_invoices/show-200.xml b/Test/Fixtures/external_invoices/show-200.xml
new file mode 100644
index 00000000..efb3a11f
--- /dev/null
+++ b/Test/Fixtures/external_invoices/show-200.xml
@@ -0,0 +1,35 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml; charset=utf-8
+
+
+
+
+
+
+ 2000000458276005
+ paid
+ USD
+ 10.45
+
+
+
+
+
+ twqswp9p3krc
+ random-ass-test-2
+ apple_app_store
+ 2023-11-15T16:16:43Z
+ 2023-11-15T16:16:43Z
+
+ the best banana in the world
+ 10.45
+ USD
+ 1
+ 2023-11-15T16:16:43Z
+ 2023-11-15T16:16:43Z
+
+
+ 2023-11-14T14:15:22Z
+ 2023-11-15T16:16:43Z
+ 2023-11-15T16:16:43Z
+
\ No newline at end of file
diff --git a/Test/Fixtures/external_payment_phases/index-200.xml b/Test/Fixtures/external_payment_phases/index-200.xml
new file mode 100644
index 00000000..e523bb94
--- /dev/null
+++ b/Test/Fixtures/external_payment_phases/index-200.xml
@@ -0,0 +1,21 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml; charset=utf-8
+
+
+
+
+ twqswp627ri3
+ 2023-11-15T00:00:00Z
+ 2023-11-17T21:27:10Z
+ 1
+ 2
+ FREE_TRIAL
+ introductory
+ 2
+ TWO WEEKS
+ 1.99
+ USD
+ 2023-11-15T16:16:43Z
+ 2023-11-15T16:16:43Z
+
+
\ No newline at end of file
diff --git a/Test/Fixtures/external_payment_phases/show-200.xml b/Test/Fixtures/external_payment_phases/show-200.xml
new file mode 100644
index 00000000..281a08b0
--- /dev/null
+++ b/Test/Fixtures/external_payment_phases/show-200.xml
@@ -0,0 +1,19 @@
+HTTP/1.1 200 OK
+Content-Type: application/xml; charset=utf-8
+
+
+
+ twqswp627ri3
+ 2023-11-15T00:00:00Z
+ 2023-11-17T21:27:10Z
+ 1
+ 2
+ FREE_TRIAL
+ introductory
+ 2
+ TWO WEEKS
+ 1.99
+ USD
+ 2023-11-15T16:16:43Z
+ 2023-11-15T16:16:43Z
+
\ No newline at end of file
diff --git a/Test/List/ExternalInvoiceListTest.cs b/Test/List/ExternalInvoiceListTest.cs
index 18abb8e2..787893cf 100644
--- a/Test/List/ExternalInvoiceListTest.cs
+++ b/Test/List/ExternalInvoiceListTest.cs
@@ -1,8 +1,7 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
+using System.Xml;
using FluentAssertions;
-using Xunit;
+using Recurly.Test.Fixtures;
namespace Recurly.Test
{
@@ -11,8 +10,21 @@ public class ExternalInvoiceListTest : BaseTest
[RecurlyFact(TestEnvironment.Type.Integration)]
public void ListExternalInvoices()
{
- var externalInvoices = ExternalInvoices.List();
- externalInvoices.Should().NotBeEmpty();
+ var xmlFixture = FixtureImporter.Get(FixtureType.ExternalInvoices, "index-200").Xml;
+ var externalInvoices = new ExternalInvoiceList();
+ XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(xmlFixture));
+ externalInvoices.ReadXml(reader);
+ externalInvoices.Should().HaveCount(1);
+ var externalInvoice = externalInvoices[0];
+ externalInvoice.ExternalId.Should().Be("2000000458276005");
+ externalInvoice.State.Should().Be(ExternalInvoice.ExternalInvoiceState.Paid);
+ externalInvoice.Total.Should().Be(new decimal(10.45));
+ externalInvoice.Currency.Should().Be("USD");
+ externalInvoice.ExternalSubscriptionUuid.Should().Be("tsfnx2vn5wh6");
+ externalInvoice.ExternalPaymentPhaseUuid.Should().Be("twqswp627ri3");
+ externalInvoice.PurchasedAt.Should().NotBe(default(DateTime));
+ externalInvoice.CreatedAt.Should().NotBe(default(DateTime));
+ externalInvoice.UpdatedAt.Should().NotBe(default(DateTime));
}
}
}
diff --git a/Test/List/ExternalPaymentPhaseListTest.cs b/Test/List/ExternalPaymentPhaseListTest.cs
new file mode 100644
index 00000000..28f7bdab
--- /dev/null
+++ b/Test/List/ExternalPaymentPhaseListTest.cs
@@ -0,0 +1,37 @@
+using System;
+
+using System.Xml;
+
+using FluentAssertions;
+
+using Recurly.Test.Fixtures;
+
+namespace Recurly.Test
+{
+ public class ExternalPaymentPhaseListTest : BaseTest
+ {
+ [RecurlyFact(TestEnvironment.Type.Integration)]
+ public void ListExternalPaymentPhase()
+ {
+ var xmlFixture = FixtureImporter.Get(FixtureType.ExternalPaymentPhases, "index-200").Xml;
+ var externalPaymentPhases = new ExternalPaymentPhaseList();
+ XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(xmlFixture));
+ externalPaymentPhases.ReadXml(reader);
+ externalPaymentPhases.Should().HaveCount(1);
+ var externalPaymentPhase = externalPaymentPhases[0];
+ externalPaymentPhase.StartedAt.Should().Be(new DateTime(2023, 11, 15, 0, 0, 0, DateTimeKind.Utc));
+ externalPaymentPhase.EndsAt.Should().Be(new DateTime(2023, 11, 17, 21, 27, 10, DateTimeKind.Utc));
+ externalPaymentPhase.StartingBillingPeriodIndex.Should().Be(1);
+ externalPaymentPhase.EndingBillingPeriodIndex.Should().Be(2);
+ externalPaymentPhase.OfferType.Should().Be("FREE_TRIAL");
+ externalPaymentPhase.OfferName.Should().Be("introductory");
+ externalPaymentPhase.PeriodCount.Should().Be(2);
+ externalPaymentPhase.PeriodLength.Should().Be("TWO WEEKS");
+ externalPaymentPhase.Amount.Should().Be(new decimal(1.99));
+ externalPaymentPhase.Currency.Should().Be("USD");
+ externalPaymentPhase.CreatedAt.Should().Be(new DateTime(2023, 11, 15, 16, 16, 43, DateTimeKind.Utc));
+ externalPaymentPhase.UpdatedAt.Should().Be(new DateTime(2023, 11, 15, 16, 16, 43, DateTimeKind.Utc));
+
+ }
+ }
+}
diff --git a/Test/Recurly.Test.csproj b/Test/Recurly.Test.csproj
index 63f953e5..4fbb7ec8 100644
--- a/Test/Recurly.Test.csproj
+++ b/Test/Recurly.Test.csproj
@@ -206,6 +206,18 @@
Always
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+