Skip to content

Commit 933c543

Browse files
authored
Merge pull request #2 from no-comment/fix-latinAmericaCaribbean-currency
Fixed currency for countries in Latin America and the Caribbean
2 parents b2eb5a8 + 4c57e6f commit 933c543

File tree

5 files changed

+67
-24
lines changed

5 files changed

+67
-24
lines changed

Sources/PlutusFinancialReportSlicer/PlutusFinancialReportSlicer.swift

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,24 @@ public enum PlutusFinancialReportSlicer {
7878
}
7979
var currency = String(regexMatch[currencySymbolReference])
8080

81-
// USD can occur twice in the file: We must take special care to distinguish between USD (and their corresponding
82-
// exchange rate) for purchases made in "Americas" and in "Rest of World", as Apple calls it. Unfortunately, Apple
81+
// USD can occur three times in the file: We must take special care to distinguish between USD (and their corresponding
82+
// exchange rate) for purchases made in "Americas", in "Rest of World", and in "Latin America and the Caribbean". Unfortunately, Apple
8383
// decided to localize the aforementioned strings so they need to be looked up in a translation table. Luckily,
8484
// localized report files currently seem to be generated only for French, German, Italian and Spanish locale settings.
85-
let localizationsRoW = ["of World", "du monde", "der Welt", "del mondo", "del mundo"]
8685
if currency == "USD" {
86+
let localizationsRoW = ["of World", "du monde", "der Welt", "del mondo", "del mundo"]
8787
for localization in localizationsRoW {
8888
if currencyCol.lowercased().contains(localization.lowercased()) {
8989
currency = "USD - RoW"
90+
break
91+
}
92+
}
93+
94+
let localizationsLatAm = ["latin", "Latin America and the Caribbean", "Amérique latine et Caraïbes", "Lateinamerika und Karibik", "America Latina e Caraibi", "América Latina y el Caribe"]
95+
for localization in localizationsLatAm {
96+
if currencyCol.lowercased().contains(localization.lowercased()) {
97+
currency = "USD - LatAm"
98+
break
9099
}
91100
}
92101
}
@@ -184,6 +193,11 @@ public enum PlutusFinancialReportSlicer {
184193
if Subsidiary.restOfWorldCountries.contains(countryCode) && currency == "USD" {
185194
currencies[countryCode] = "USD - RoW"
186195
}
196+
// special case affecting countries Apple put in the "Latin America and the Caribbean" group: currency for those is listed as "USD"
197+
// in the sales reports but the corresponding exchange rate is keyed "USD - LatAm"
198+
if Subsidiary.latinAmericaCaribbeanCountries.contains(countryCode) && currency == "USD" {
199+
currencies[countryCode] = "USD - LatAm"
200+
}
187201
}
188202

189203
// break if we didn't read any meaningful data
@@ -249,9 +263,6 @@ public enum PlutusFinancialReportSlicer {
249263
invoiceItems.append(Invoice.InvoiceItem(quantity: quantity, product: product.product, amount: amount, exchangeRate: exchangeRate, amountInLocalCurrency: amountInLocalCurrency, dateRange: dateRange))
250264
}
251265

252-
// although of course rounding happens here, too, it won't show because Apple converts currencies in the same per country manner
253-
let countrySumInLocalCurrency = countrySum * exchangeRate
254-
255266
countrySplitting.append(Invoice.SubInvoice(country: country, countryCode: countryCode, countryCurrency: countryCurrency, invoiceItems: invoiceItems))
256267
}
257268

Sources/PlutusFinancialReportSlicer/Subsidiary.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,12 @@ public extension Subsidiary {
319319
static let restOfWorldCountries: [String] = [
320320
"AF", "AL", "DZ", "AO", "AM", "AZ", "BH", "BY", "BJ", "BT", "BW", "BN", "BF", "KH", "CM", "CV", "TD", "CG", "CD", "CI", "HR", "EG", "FJ", "GA", "GM", "GE", "GH", "GW", "IS", "IQ", "JO", "KZ", "KE", "KR", "KW", "KG", "LA", "LB", "LR", "LY", "MO", "MK", "MG", "MW", "MY", "MV", "ML", "MR", "MU", "FM", "MD", "MA", "MZ", "MM", "NA", "NR", "NP", "NE", "NG", "OM", "PK", "PW", "PG", "PH", "QA", "RW", "ST", "SN", "SC", "SL", "SB", "LK", "SZ", "TJ", "TZ", "TO", "TN", "TM", "UG", "UA", "UZ", "VU", "VN", "YE", "ZM", "ZW"
321321
]
322+
323+
// https://developer.apple.com/help/app-store-connect/reference/financial-report-regions-and-currencies
324+
// not for corporations but for currency conversion
325+
static let latinAmericaCaribbeanCountries: [String] = [
326+
"AI", "AG", "AR", "BS", "BB", "BZ", "BM", "BO", "BR", "VG", "KY", "CL", "CR", "DM", "DO", "EC", "SV", "GD", "GY", "GT", "HN", "JM", "MS", "NI", "PA", "PY", "KN", "LC", "VC", "SR", "TT", "TC", "UY", "VE"
327+
]
322328
}
323329

324330
enum LookupError: Error {

Tests/PlutusFinancialReportSlicerTests/PlutusFinancialReportSlicerTests.swift

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ final class PlutusFinancialReportSlicerTests: XCTestCase {
1313
["Switzerland (CHF)", "29", "33.15", "33.15", "0", "0", "0", "33.15", "0.80030", "26.53", "EUR", ""],
1414
["Euro-Zone (EUR)", "19", "206.89", "206.89", "0", "0", "0", "206.89", "1.00000", "206.89", "EUR", ""],
1515
["Japan (JPY)", "2", "179", "179", "0", "0", "-37", "142", "0.00817", "1.16", "EUR", ""],
16+
["Americas (USD)", "42", "336.78", "336.78", "0", "0", "0", "336.78", "0.91956", "309.69", "EUR", ""],
17+
["Latin America and the Caribbean (USD)", "1", "5.09", "5.09", "0", "0", "0", "5.09", "0.91945", "4.68", "EUR", ""],
18+
["Rest of World (USD)", "1", "4.95", "4.95", "0", "0", "0", "4.95", "0.91919", "4.55", "EUR", ""],
1619
["", "", "", "", "", "", "", "", "", "", "", "", ""],
1720
["", "", "", "", "", "", "", "", "", "", "234.58 EUR", "", ""],
1821
["", "", "", "", "", "", "", "", "", "", "Paid to FICTIONAL BANK -****1299", "", ""],
@@ -39,19 +42,15 @@ final class PlutusFinancialReportSlicerTests: XCTestCase {
3942
let splits = try PlutusFinancialReportSlicer.splitSalesByCorporation(sales: financialReportsData.sales, dateRange: dateRange, currencyData: currencyData)
4043
print(splits)
4144

42-
XCTAssertEqual(splits.count, 2)
43-
guard splits.count == 2 else { return }
45+
XCTAssertEqual(splits.count, 4)
46+
guard splits.count == 4 else { return }
4447

45-
let jpCorp = splits.first(where: { $0.recipient.adress.contains("Japan") })!
46-
let euCorp = splits.first(where: { $0.recipient.adress.contains("Ireland") })!
47-
48-
XCTAssertEqual(jpCorp.recipient, .japan)
49-
XCTAssertEqual(euCorp.recipient, .europe)
48+
let jpCorp = splits.first(where: { $0.recipient == .japan })!
49+
let euCorp = splits.first(where: { $0.recipient == .europe })!
5050

5151
XCTAssertEqual(jpCorp.totalInLocalCurrency, 1.16, accuracy: 0.000001)
52-
XCTAssertEqual(euCorp.totalInLocalCurrency, 233.42, accuracy: 0.000001)
52+
XCTAssertEqual(euCorp.totalInLocalCurrency, 237.97, accuracy: 0.000001)
5353

54-
// TODO: improve floating points only for 3 digits
5554
AssertSameCountrySplitting(jpCorp.countrySplitting, [
5655
Invoice.SubInvoice(country: "Japan", countryCode: "JP", countryCurrency: "JPY", invoiceItems: [
5756
Invoice.InvoiceItem(quantity: 1, product: "Example App 3", amount: 94.4022346368715, exchangeRate: 0.008169014084507042, amountInLocalCurrency: 0.7711731843575418, dateRange: dateRange),
@@ -76,6 +75,9 @@ final class PlutusFinancialReportSlicerTests: XCTestCase {
7675
Invoice.InvoiceItem(quantity: 2, product: "Example App 6", amount: 24.34, exchangeRate: 1.0, amountInLocalCurrency: 24.34, dateRange: dateRange),
7776
Invoice.InvoiceItem(quantity: 15, product: "Example App 5", amount: 158.21, exchangeRate: 1.0, amountInLocalCurrency: 158.21, dateRange: dateRange),
7877
]),
78+
Invoice.SubInvoice(country: "Ukraine", countryCode: "UA", countryCurrency: "USD - RoW", invoiceItems: [
79+
Invoice.InvoiceItem(quantity: 1, product: "Example App 5", amount: 4.95, exchangeRate: 0.9191919191919191, amountInLocalCurrency: 4.55, dateRange: dateRange),
80+
]),
7981
])
8082
}
8183

@@ -94,12 +96,21 @@ final class PlutusFinancialReportSlicerTests: XCTestCase {
9496
case "JPY":
9597
XCTAssertEqual(data.exchangeRate, 0.008169014084507042253521126761, accuracy: 0.000001)
9698
XCTAssertEqual(data.taxFactor, 0.7932960893854748603351955307, accuracy: 0.000001)
99+
case "USD":
100+
XCTAssertEqual(data.exchangeRate, 0.9195617316942812, accuracy: 0.000001)
101+
XCTAssertEqual(data.taxFactor, 1, accuracy: 0.000001)
102+
case "USD - RoW":
103+
XCTAssertEqual(data.exchangeRate, 0.9191919191919191, accuracy: 0.000001)
104+
XCTAssertEqual(data.taxFactor, 1, accuracy: 0.000001)
105+
case "USD - LatAm":
106+
XCTAssertEqual(data.exchangeRate, 0.91944990176817287, accuracy: 0.000001)
107+
XCTAssertEqual(data.taxFactor, 1, accuracy: 0.000001)
97108
default:
98-
XCTFail()
109+
XCTFail(data.currency)
99110
}
100111
}
101112

102-
XCTAssertEqual(currencyData.count, 3)
113+
XCTAssertEqual(currencyData.count, 6)
103114
}
104115

105116
func testParseFinancialReports() throws {
@@ -131,23 +142,32 @@ final class PlutusFinancialReportSlicerTests: XCTestCase {
131142
case "FR":
132143
AssertSameProductSales(countrySales.sales, [ProductSale(product: "Example App 5", quantity: 1, amount: 12.17)])
133144
XCTAssertEqual(countrySales.currency, "EUR")
145+
case "CA":
146+
AssertSameProductSales(countrySales.sales, [ProductSale(product: "Example App 5", quantity: 1, amount: 10.2)])
147+
XCTAssertEqual(countrySales.currency, "USD")
148+
case "UA":
149+
AssertSameProductSales(countrySales.sales, [ProductSale(product: "Example App 5", quantity: 1, amount: 4.95)])
150+
XCTAssertEqual(countrySales.currency, "USD - RoW")
151+
case "CR":
152+
AssertSameProductSales(countrySales.sales, [ProductSale(product: "Example App 5", quantity: 1, amount: 5.09)])
153+
XCTAssertEqual(countrySales.currency, "USD - LatAm")
134154
default:
135-
XCTFail()
155+
XCTFail(countrySales.countryCode)
136156
}
137157
}
138158

139-
XCTAssertEqual(financialReportsData.sales.count, 5)
159+
XCTAssertEqual(financialReportsData.sales.count, 8)
140160
let dateFormatter = DateFormatter()
141161
dateFormatter.dateFormat = "MM/dd/yyyy"
142162
XCTAssertEqual(financialReportsData.dateRange, DateInterval(start: dateFormatter.date(from: "08/31/2014")!, end: dateFormatter.date(from: "09/27/2014")!))
143163
}
144164

145-
fileprivate func AssertSameCountrySplitting(_ a: [Invoice.SubInvoice], _ b: [Invoice.SubInvoice]) {
146-
XCTAssertEqual(Set(a), Set(b))
165+
fileprivate func AssertSameCountrySplitting(_ a: [Invoice.SubInvoice], _ b: [Invoice.SubInvoice], file: StaticString = #file, line: UInt = #line) {
166+
XCTAssertEqual(Set(a), Set(b), file: file, line: line)
147167
}
148168

149-
fileprivate func AssertSameProductSales(_ a: [ProductSale], _ b: [ProductSale]) {
150-
XCTAssertEqual(Set(a), Set(b))
169+
fileprivate func AssertSameProductSales(_ a: [ProductSale], _ b: [ProductSale], file: StaticString = #file, line: UInt = #line) {
170+
XCTAssertEqual(Set(a), Set(b), file: file, line: line)
151171
}
152172

153173
fileprivate func readFile(url: URL) throws -> String {

Tests/PlutusFinancialReportSlicerTests/Resources/45545510_0914.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ Start Date End Date UPC ISRC/ISBN Vendor Identifier Quantity Partner Share Exten
1111
08/31/2014 09/27/2014 Example App 6 2 12.17 24.34 EUR S 238100296 Developer Example App 6 F1 DE 19.99 EUR
1212
08/31/2014 09/27/2014 Example App 5 14 12.17 170.38 EUR S 258801387 Developer Example App 5 F1 DE 19.99 EUR
1313
08/31/2014 09/27/2014 Example App 5 1 12.17 12.17 EUR S 258801387 Developer Example App 5 F1 FR 19.99 EUR
14-
Total_Rows 12
14+
08/31/2014 09/27/2014 Example App 5 1 5.09 5.09 USD S 258801387 Developer Example App 5 F1 CR 5.99 USD
15+
08/31/2014 09/27/2014 Example App 5 1 4.95 4.95 USD S 258801387 Developer Example App 5 F1 UA 6.99 USD
16+
08/31/2014 09/27/2014 Example App 5 1 10.20 10.20 USD S 258801387 Developer Example App 5 F1 CA 12.00 USD
17+
Total_Rows 15
1518
Country Of Sale Partner Share Currency Quantity Extended Partner Share
1619
AU AUD 1 10.81
1720
BR BRL 1 4.17

Tests/PlutusFinancialReportSlicerTests/Resources/financial_report.csv

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ Region (Currency),Units Sold,Earned,Pre-Tax Subtotal,Input Tax,Adjustments,Withh
44
Switzerland (CHF),"29","33.15","33.15","0","0","0","33.15","0.80030","26.53","EUR",
55
Euro-Zone (EUR),"19","206.89","206.89","0","0","0","206.89","1.00000","206.89","EUR",
66
Japan (JPY),"2","179","179","0","0","-37","142","0.00817","1.16","EUR",
7+
"Americas (USD)","42","336.78","336.78","0","0","0","336.78","0.91956","309.69","EUR",
8+
"Latin America and the Caribbean (USD)","1","5.09","5.09","0","0","0","5.09","0.91945","4.68","EUR",
9+
"Rest of World (USD)","1","4.95","4.95","0","0","0","4.95","0.91919","4.55","EUR",
710
,,,,,,,,,,,,
811
,,,,,,,,,,"234.58" EUR,,
912
,,,,,,,,,,Paid to FICTIONAL BANK -****1299,,

0 commit comments

Comments
 (0)