Skip to content

Commit 1f82878

Browse files
author
patryk.bujalla
committed
extend where with deep filter
1 parent 6ec0e0b commit 1f82878

9 files changed

Lines changed: 368 additions & 5 deletions

File tree

PortaCapena.OdooJsonRpcClient.Example/OdooClientRequests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public async Task Get_product_by_Id_test()
7272
var odooClient = new OdooClient(Config);
7373

7474
var query = OdooQuery<ProductProductOdooDto>.Create()
75-
.Where(x => x.Barcode, OdooOperator.EqualsTo, 1);
75+
.Where(x => x.Id, OdooOperator.EqualsTo, 303);
7676

7777
var products = await odooClient.GetAsync<ProductProductOdooDto>(query);
7878

@@ -187,7 +187,7 @@ public async Task Should_get_products_with_selected_properties_using_query()
187187
public async Task Get_DotNet_model_should_return_string()
188188
{
189189
var odooClient = new OdooClient(Config);
190-
var tableName = "res.country";
190+
var tableName = "account.account";
191191
var modelResult = await odooClient.GetModelAsync(tableName);
192192

193193
modelResult.Succeed.Should().BeTrue();
@@ -533,9 +533,9 @@ public async Task CreatePurchaseOrderAsync()
533533
// orderResult.Succeed.Should().BeTrue();
534534

535535

536-
// var taxId = odooCompanyTaxes.Value.FirstOrDefault(x => x.Amount == 6);
536+
// var taxId = odooCompanyTaxes.Value.FirstOrDefault(x => x.Amount == 6);
537537

538-
// taxId.Should().NotBeNull();
538+
// taxId.Should().NotBeNull();
539539

540540
var model = OdooDictionaryModel.Create(() => new PurchaseOrderLineOdooModel()
541541
{
@@ -544,7 +544,7 @@ public async Task CreatePurchaseOrderAsync()
544544
ProductQty = 11,
545545
// OrderId = orderResult.Value,
546546
State = StatusPurchaseOrderLineOdooEnum.PurchaseOrder,
547-
TaxesId = new long[] {12, 11}
547+
TaxesId = new long[] { 12, 11 }
548548
});
549549

550550
// var createPurchaseOrderLineResult = await purchaseOrderLineOdooRepository.CreateAsync(model);

PortaCapena.OdooJsonRpcClient.Example/OdooRepositoryRequests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Threading.Tasks;
22
using FluentAssertions;
3+
using PortaCapena.OdooJsonRpcClient.Consts;
34
using PortaCapena.OdooJsonRpcClient.Shared;
45
using PortaCapena.OdooJsonRpcClient.Shared.Models;
56
using Xunit;
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
using System;
2+
using System.Runtime.Serialization;
3+
using Newtonsoft.Json;
4+
using Newtonsoft.Json.Converters;
5+
using PortaCapena.OdooJsonRpcClient.Attributes;
6+
using PortaCapena.OdooJsonRpcClient.Converters;
7+
using PortaCapena.OdooJsonRpcClient.Models;
8+
9+
namespace PortaCapena.OdooJsonRpcClient.Shared.Models
10+
{
11+
[OdooTableName("account.account")]
12+
[JsonConverter(typeof(OdooModelConverter))]
13+
public class AccountAccountOdooModel : IOdooModel
14+
{
15+
16+
// res.currency
17+
[JsonProperty("currency_id")]
18+
public long? CurrencyId { get; set; }
19+
20+
// required
21+
[JsonProperty("code")]
22+
public string Code { get; set; }
23+
24+
[JsonProperty("deprecated")]
25+
public bool? Deprecated { get; set; }
26+
27+
[JsonProperty("used")]
28+
public bool? Used { get; set; }
29+
30+
// account.account.type
31+
// required
32+
[JsonProperty("user_type_id")]
33+
public long UserTypeId { get; set; }
34+
35+
[JsonProperty("internal_type")]
36+
public InternalTypeAccountAccountOdooEnum? InternalType { get; set; }
37+
38+
[JsonProperty("internal_group")]
39+
public InternalGroupAccountAccountOdooEnum? InternalGroup { get; set; }
40+
41+
[JsonProperty("reconcile")]
42+
public bool? Reconcile { get; set; }
43+
44+
// account.tax
45+
[JsonProperty("tax_ids")]
46+
public long[] TaxIds { get; set; }
47+
48+
[JsonProperty("note")]
49+
public string Note { get; set; }
50+
51+
// res.company
52+
// required
53+
[JsonProperty("company_id")]
54+
public long CompanyId { get; set; }
55+
56+
// account.account.tag
57+
[JsonProperty("tag_ids")]
58+
public long[] TagIds { get; set; }
59+
60+
// account.group
61+
[JsonProperty("group_id")]
62+
public long? GroupId { get; set; }
63+
64+
// account.root
65+
[JsonProperty("root_id")]
66+
public long? RootId { get; set; }
67+
68+
// account.journal
69+
[JsonProperty("allowed_journal_ids")]
70+
public long[] AllowedJournalIds { get; set; }
71+
72+
[JsonProperty("opening_debit")]
73+
public decimal? OpeningDebit { get; set; }
74+
75+
[JsonProperty("opening_credit")]
76+
public decimal? OpeningCredit { get; set; }
77+
78+
[JsonProperty("opening_balance")]
79+
public decimal? OpeningBalance { get; set; }
80+
81+
[JsonProperty("is_off_balance")]
82+
public bool? IsOffBalance { get; set; }
83+
84+
// required
85+
[JsonProperty("name")]
86+
public string Name { get; set; }
87+
88+
// res.currency
89+
[JsonProperty("exclude_provision_currency_ids")]
90+
public long[] ExcludeProvisionCurrencyIds { get; set; }
91+
92+
// account.asset
93+
[JsonProperty("asset_model")]
94+
public long? AssetModel { get; set; }
95+
96+
// required
97+
[JsonProperty("create_asset")]
98+
public CreateAssetAccountAccountOdooEnum CreateAsset { get; set; }
99+
100+
[JsonProperty("can_create_asset")]
101+
public bool? CanCreateAsset { get; set; }
102+
103+
[JsonProperty("form_view_ref")]
104+
public string FormViewRef { get; set; }
105+
106+
[JsonProperty("asset_type")]
107+
public AssetTypeAccountAccountOdooEnum? AssetType { get; set; }
108+
109+
[JsonProperty("multiple_assets_per_line")]
110+
public bool? MultipleAssetsPerLine { get; set; }
111+
112+
[JsonProperty("id")]
113+
public long Id { get; set; }
114+
115+
[JsonProperty("display_name")]
116+
public string DisplayName { get; set; }
117+
118+
// res.users
119+
[JsonProperty("create_uid")]
120+
public long? CreateUid { get; set; }
121+
122+
[JsonProperty("create_date")]
123+
public DateTime? CreateDate { get; set; }
124+
125+
// res.users
126+
[JsonProperty("write_uid")]
127+
public long? WriteUid { get; set; }
128+
129+
[JsonProperty("write_date")]
130+
public DateTime? WriteDate { get; set; }
131+
132+
[JsonProperty("__last_update")]
133+
public DateTime? LastUpdate { get; set; }
134+
}
135+
136+
137+
// The 'Internal Type' is used for features available on different types of accounts: liquidity type is for cash or bank accounts, payable/receivable is for vendor/customer accounts.
138+
[JsonConverter(typeof(StringEnumConverter))]
139+
public enum InternalTypeAccountAccountOdooEnum
140+
{
141+
[EnumMember(Value = "other")]
142+
Regular = 1,
143+
144+
[EnumMember(Value = "receivable")]
145+
Receivable = 2,
146+
147+
[EnumMember(Value = "payable")]
148+
Payable = 3,
149+
150+
[EnumMember(Value = "liquidity")]
151+
Liquidity = 4,
152+
}
153+
154+
155+
// The 'Internal Group' is used to filter accounts based on the internal group set on the account type.
156+
[JsonConverter(typeof(StringEnumConverter))]
157+
public enum InternalGroupAccountAccountOdooEnum
158+
{
159+
[EnumMember(Value = "equity")]
160+
Equity = 1,
161+
162+
[EnumMember(Value = "asset")]
163+
Asset = 2,
164+
165+
[EnumMember(Value = "liability")]
166+
Liability = 3,
167+
168+
[EnumMember(Value = "income")]
169+
Income = 4,
170+
171+
[EnumMember(Value = "expense")]
172+
Expense = 5,
173+
174+
[EnumMember(Value = "off_balance")]
175+
OffBalance = 6,
176+
}
177+
178+
179+
[JsonConverter(typeof(StringEnumConverter))]
180+
public enum CreateAssetAccountAccountOdooEnum
181+
{
182+
[EnumMember(Value = "no")]
183+
No = 1,
184+
185+
[EnumMember(Value = "draft")]
186+
CreateInDraft = 2,
187+
188+
[EnumMember(Value = "validate")]
189+
CreateAndValidate = 3,
190+
}
191+
192+
193+
[JsonConverter(typeof(StringEnumConverter))]
194+
public enum AssetTypeAccountAccountOdooEnum
195+
{
196+
[EnumMember(Value = "sale")]
197+
DeferredRevenue = 1,
198+
199+
[EnumMember(Value = "expense")]
200+
DeferredExpense = 2,
201+
202+
[EnumMember(Value = "purchase")]
203+
Asset = 3,
204+
}
205+
206+
}

PortaCapena.OdooJsonRpcClient.Tests/OdooQueryBuilderTests.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using System.Linq;
34
using FluentAssertions;
45
using Newtonsoft.Json;
@@ -137,5 +138,49 @@ public void When_use_select_with_anymous_model_shoud_return_correct_filters_mode
137138
filters.ReturnFields.Skip(1).First().Should().Be("description");
138139
filters.ReturnFields.Skip(2).First().Should().Be("write_date");
139140
}
141+
142+
[Fact]
143+
public void When_use_pridicate_where_with_one_forgin_key_shoud_return_correct_filters_model()
144+
{
145+
var filters = OdooQuery<ProductProductOdooDto>.Create()
146+
.Where<ResCompanyOdooModel>(x => x.CompanyId, x => x.CountryCode, OdooOperator.EqualsTo, "BE");
147+
148+
filters.Filters.Count.Should().Be(1);
149+
filters.Filters[0].Should().NotBeNull();
150+
151+
var arr = filters.Filters[0] as ArrayList;
152+
arr[0].Should().Be("company_id.country_code");
153+
arr[1].Should().Be("=");
154+
arr[2].Should().Be("BE");
155+
156+
var json = JsonConvert.SerializeObject(filters.Filters);
157+
json.Should().Be("[[\"company_id.country_code\",\"=\",\"BE\"]]");
158+
159+
filters.ReturnFields.Count.Should().Be(0);
160+
filters.Limit.Should().BeNull();
161+
filters.Offset.Should().BeNull();
162+
}
163+
164+
[Fact]
165+
public void When_use_pridicate_where_with_two_forgin_key_shoud_return_correct_filters_model()
166+
{
167+
var filters = OdooQuery<ProductProductOdooDto>.Create()
168+
.Where<ResCompanyOdooModel, AccountTaxOdooModel>(x => x.PropertyAccountExpenseId, x => x.AccountSaleTaxId, x => x.CountryCode, OdooOperator.EqualsTo, "BE");
169+
170+
filters.Filters.Count.Should().Be(1);
171+
filters.Filters[0].Should().NotBeNull();
172+
173+
var arr = filters.Filters[0] as ArrayList;
174+
arr[0].Should().Be("property_account_expense_id.account_sale_tax_id.country_code");
175+
arr[1].Should().Be("=");
176+
arr[2].Should().Be("BE");
177+
178+
var json = JsonConvert.SerializeObject(filters.Filters);
179+
json.Should().Be("[[\"property_account_expense_id.account_sale_tax_id.country_code\",\"=\",\"BE\"]]");
180+
181+
filters.ReturnFields.Count.Should().Be(0);
182+
filters.Limit.Should().BeNull();
183+
filters.Offset.Should().BeNull();
184+
}
140185
}
141186
}

PortaCapena.OdooJsonRpcClient/Converters/OdooExpresionMapper.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,27 @@ internal static string GetPropertyName<T>(Expression<Func<T, Enum>> expression)
5353
return null;
5454
}
5555

56+
internal static string GetPropertyName<T>(Expression<Func<T, long>> expression) where T : IOdooAtributtesModel
57+
{
58+
if (expression.Body is MemberExpression body)
59+
return body.Member.Name;
60+
61+
if (expression.Body is UnaryExpression unar && unar.Operand is MemberExpression member)
62+
return member.Member.Name;
63+
64+
return null;
65+
}
66+
67+
internal static string GetPropertyName<T>(Expression<Func<T, long?>> expression) where T : IOdooAtributtesModel
68+
{
69+
if (expression.Body is MemberExpression body)
70+
return body.Member.Name;
71+
72+
if (expression.Body is UnaryExpression unar && unar.Operand is MemberExpression member)
73+
return member.Member.Name;
74+
75+
return null;
76+
}
77+
5678
}
5779
}

PortaCapena.OdooJsonRpcClient/Models/OdooClientContext.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public class OdooClientContext
1010
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
1111
public string Timezone { get; set; }
1212

13+
// https://stackoverflow.com/questions/46586281/alter-odoo-xmlrpc-context-to-use-a-specific-language
14+
// https://gist.github.com/ilyasProgrammer/cf6647356c9a3722f597f72b7685a4c3
1315
public OdooClientContext()
1416
{
1517
}

PortaCapena.OdooJsonRpcClient/Request/OdooQueryBuilder.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,37 @@ public OdooQueryBuilder<T> Where(Expression<Func<T, object>> expression, OdooOpe
5353
_query.Where(expression, odooOperator, value);
5454
return this;
5555
}
56+
public OdooQueryBuilder<T> Where<TForeignKeyLevel1>(Expression<Func<T, long>> expression, Expression<Func<TForeignKeyLevel1, object>> expressionForeignKeyLevel1, OdooOperator odooOperator, object value) where TForeignKeyLevel1 : IOdooModel, new()
57+
{
58+
_query.Where(expression, expressionForeignKeyLevel1, odooOperator, value);
59+
return this;
60+
}
61+
public OdooQueryBuilder<T> Where<TForeignKeyLevel1>(Expression<Func<T, long?>> expression, Expression<Func<TForeignKeyLevel1, object>> expressionForeignKeyLevel1, OdooOperator odooOperator, object value) where TForeignKeyLevel1 : IOdooModel, new()
62+
{
63+
_query.Where(expression, expressionForeignKeyLevel1, odooOperator, value);
64+
return this;
65+
}
66+
67+
public OdooQueryBuilder<T> Where<TForeignKeyLevel1, TForeignKeyLevel2>(Expression<Func<T, long>> expression, Expression<Func<TForeignKeyLevel1, long>> expressionForeignKeyLevel1, Expression<Func<TForeignKeyLevel2, object>> expressionForeignKeyLevel2, OdooOperator odooOperator, object value) where TForeignKeyLevel1 : IOdooModel, new() where TForeignKeyLevel2 : IOdooModel, new()
68+
{
69+
_query.Where(expression, expressionForeignKeyLevel1, expressionForeignKeyLevel2, odooOperator, value);
70+
return this;
71+
}
72+
public OdooQueryBuilder<T> Where<TForeignKeyLevel1, TForeignKeyLevel2>(Expression<Func<T, long?>> expression, Expression<Func<TForeignKeyLevel1, long?>> expressionForeignKeyLevel1, Expression<Func<TForeignKeyLevel2, object>> expressionForeignKeyLevel2, OdooOperator odooOperator, object value) where TForeignKeyLevel1 : IOdooModel, new() where TForeignKeyLevel2 : IOdooModel, new()
73+
{
74+
_query.Where(expression, expressionForeignKeyLevel1, expressionForeignKeyLevel2, odooOperator, value);
75+
return this;
76+
}
77+
public OdooQueryBuilder<T> Where<TForeignKeyLevel1, TForeignKeyLevel2>(Expression<Func<T, long>> expression, Expression<Func<TForeignKeyLevel1, long?>> expressionForeignKeyLevel1, Expression<Func<TForeignKeyLevel2, object>> expressionForeignKeyLevel2, OdooOperator odooOperator, object value) where TForeignKeyLevel1 : IOdooModel, new() where TForeignKeyLevel2 : IOdooModel, new()
78+
{
79+
_query.Where(expression, expressionForeignKeyLevel1, expressionForeignKeyLevel2, odooOperator, value);
80+
return this;
81+
}
82+
public OdooQueryBuilder<T> Where<TForeignKeyLevel1, TForeignKeyLevel2>(Expression<Func<T, long?>> expression, Expression<Func<TForeignKeyLevel1, long>> expressionForeignKeyLevel1, Expression<Func<TForeignKeyLevel2, object>> expressionForeignKeyLevel2, OdooOperator odooOperator, object value) where TForeignKeyLevel1 : IOdooModel, new() where TForeignKeyLevel2 : IOdooModel, new()
83+
{
84+
_query.Where(expression, expressionForeignKeyLevel1, expressionForeignKeyLevel2, odooOperator, value);
85+
return this;
86+
}
5687

5788
public OdooQueryBuilder<T> ById(long id)
5889
{

0 commit comments

Comments
 (0)