diff --git a/applications/accounting/servicedef/secas.xml b/applications/accounting/servicedef/secas.xml
index 93bd386e5ac..706f73ce3d4 100644
--- a/applications/accounting/servicedef/secas.xml
+++ b/applications/accounting/servicedef/secas.xml
@@ -47,6 +47,19 @@ under the License.
+
+
+
+
+
+
+
+
+
+
+
+
@@ -105,11 +118,25 @@ under the License.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgBillingAccountTests.groovy b/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgBillingAccountTests.groovy
new file mode 100644
index 00000000000..95a276d0d1b
--- /dev/null
+++ b/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgBillingAccountTests.groovy
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.apache.ofbiz.accounting.accounting
+
+import org.apache.ofbiz.entity.GenericValue
+import org.apache.ofbiz.service.ServiceUtil
+import org.apache.ofbiz.testtools.JunitJupiterTest
+import org.apache.ofbiz.testtools.JupiterTestHelper
+import org.junit.jupiter.api.Order
+import org.junit.jupiter.api.Test
+
+@JunitJupiterTest
+class AutoAcctgBillingAccountTests implements JupiterTestHelper {
+
+ private String billingAccountId
+
+ @Test
+ @Order(1)
+ void testCreateBillingAccount() {
+ Map serviceCtx = [
+ accountLimit: 1000,
+ description: 'AutoAcctgBillingAccountTests billing account',
+ userLogin: userLogin
+ ]
+ Map serviceResult = dispatcher.runSync('createBillingAccount', serviceCtx)
+ assert ServiceUtil.isSuccess(serviceResult)
+ billingAccountId = serviceResult.billingAccountId
+ assert billingAccountId
+
+ GenericValue billingAccount = from('BillingAccount').where('billingAccountId', billingAccountId).queryOne()
+ assert billingAccount
+ }
+
+ // DEMO_COMPANY already holds the INTERNAL_ORGANIZATIO role (see AccountingTestsData.xml), so this
+ // combination is legitimate and must still succeed after OFBIZ-12372's validation is in place.
+ @Test
+ @Order(2)
+ void testCreateBillingAccountRole() {
+ String partyId = testParams.partyId ?: 'DEMO_COMPANY'
+ String roleTypeId = 'INTERNAL_ORGANIZATIO'
+ Map serviceCtx = [
+ billingAccountId: billingAccountId,
+ partyId: partyId,
+ roleTypeId: roleTypeId,
+ userLogin: userLogin
+ ]
+ Map serviceResult = dispatcher.runSync('createBillingAccountRole', serviceCtx)
+ assert ServiceUtil.isSuccess(serviceResult)
+
+ GenericValue billingAccountRole = from('BillingAccountRole')
+ .where('billingAccountId', billingAccountId, 'partyId', partyId, 'roleTypeId', roleTypeId)
+ .queryOne()
+ assert billingAccountRole
+ }
+
+ // OFBIZ-12372: DEMO_COMPANY does not hold the CARRIER role (only INTERNAL_ORGANIZATIO/SUPPLIER,
+ // see AccountingTestsData.xml), so this combination must be rejected instead of the
+ // createBillingAccountRole -> ensurePartyRole eca silently fabricating a PartyRole record for a
+ // role the party was never given.
+ @Test
+ @Order(3)
+ void testCreateBillingAccountRoleRejectsPartyWithoutRole() {
+ String partyId = testParams.partyId ?: 'DEMO_COMPANY'
+ Map serviceCtx = [
+ billingAccountId: billingAccountId,
+ partyId: partyId,
+ roleTypeId: 'CARRIER',
+ userLogin: userLogin
+ ]
+ Map serviceResult = dispatcher.runSync('createBillingAccountRole', serviceCtx)
+ assert ServiceUtil.isError(serviceResult)
+
+ GenericValue billingAccountRole = from('BillingAccountRole')
+ .where('billingAccountId', billingAccountId, 'partyId', partyId, 'roleTypeId', 'CARRIER')
+ .queryOne()
+ assert !billingAccountRole
+
+ GenericValue spuriousPartyRole = from('PartyRole')
+ .where('partyId', partyId, 'roleTypeId', 'CARRIER')
+ .queryOne()
+ assert !spuriousPartyRole
+ }
+
+}
diff --git a/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgBudgetTests.groovy b/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgBudgetTests.groovy
index 44dc27f990b..d87c09d5e95 100644
--- a/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgBudgetTests.groovy
+++ b/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgBudgetTests.groovy
@@ -62,4 +62,56 @@ class AutoAcctgBudgetTests implements JupiterTestHelper {
assert budgetStatuses[0].statusId == statusId
}
+ // DEMO_COMPANY already holds the INTERNAL_ORGANIZATIO role (see AccountingTestsData.xml), so this
+ // combination is legitimate and must still succeed after OFBIZ-12371's validation is in place.
+ @Test
+ @Order(3)
+ void testCreateBudgetRole() {
+ String budgetId = testParams.budgetId ?: '9999'
+ String partyId = testParams.partyId ?: 'DEMO_COMPANY'
+ String roleTypeId = 'INTERNAL_ORGANIZATIO'
+ Map serviceCtx = [
+ budgetId: budgetId,
+ partyId: partyId,
+ roleTypeId: roleTypeId,
+ userLogin: userLogin
+ ]
+ Map serviceResult = dispatcher.runSync('createBudgetRole', serviceCtx)
+ assert ServiceUtil.isSuccess(serviceResult)
+
+ GenericValue budgetRole = from('BudgetRole')
+ .where('budgetId', budgetId, 'partyId', partyId, 'roleTypeId', roleTypeId)
+ .queryOne()
+ assert budgetRole
+ }
+
+ // OFBIZ-12371: DEMO_COMPANY does not hold the CARRIER role (only INTERNAL_ORGANIZATIO/SUPPLIER,
+ // see AccountingTestsData.xml), so this combination must be rejected instead of the
+ // createBudgetRole -> ensurePartyRole eca silently fabricating a PartyRole record for a role
+ // the party was never given.
+ @Test
+ @Order(4)
+ void testCreateBudgetRoleRejectsPartyWithoutRole() {
+ String budgetId = testParams.budgetId ?: '9999'
+ String partyId = testParams.partyId ?: 'DEMO_COMPANY'
+ Map serviceCtx = [
+ budgetId: budgetId,
+ partyId: partyId,
+ roleTypeId: 'CARRIER',
+ userLogin: userLogin
+ ]
+ Map serviceResult = dispatcher.runSync('createBudgetRole', serviceCtx)
+ assert ServiceUtil.isError(serviceResult)
+
+ GenericValue budgetRole = from('BudgetRole')
+ .where('budgetId', budgetId, 'partyId', partyId, 'roleTypeId', 'CARRIER')
+ .queryOne()
+ assert !budgetRole
+
+ GenericValue spuriousPartyRole = from('PartyRole')
+ .where('partyId', partyId, 'roleTypeId', 'CARRIER')
+ .queryOne()
+ assert !spuriousPartyRole
+ }
+
}
diff --git a/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgFinAccountTests.groovy b/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgFinAccountTests.groovy
index c07e89984c6..0cfb8f2f65d 100644
--- a/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgFinAccountTests.groovy
+++ b/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgFinAccountTests.groovy
@@ -114,8 +114,38 @@ class AutoAcctgFinAccountTests implements JupiterTestHelper {
assert finAccountRole
}
+ // OFBIZ-12373: DEMO_COMPANY does not hold the CARRIER role (only INTERNAL_ORGANIZATIO/SUPPLIER,
+ // see AccountingTestsData.xml), so this combination must be rejected instead of the createFinAccountRole
+ // -> ensurePartyRole eca silently fabricating a PartyRole record for a role the party was never given.
@Test
@Order(5)
+ void testCreateFinAccountRoleRejectsPartyWithoutRole() {
+ String finAccountId = testParams.finAccountId ?: '1003'
+ String partyId = testParams.partyId ?: 'DEMO_COMPANY'
+ Map serviceCtx = [
+ finAccountId: finAccountId,
+ partyId: partyId,
+ roleTypeId: 'CARRIER',
+ fromDate: UtilDateTime.nowTimestamp(),
+ currencyUomId: testParams.currencyUomId ?: 'USD',
+ userLogin: userLogin
+ ]
+ Map serviceResult = dispatcher.runSync('createFinAccountRole', serviceCtx)
+ assert ServiceUtil.isError(serviceResult)
+
+ GenericValue finAccountRole = from('FinAccountRole')
+ .where('finAccountId', finAccountId, 'partyId', partyId, 'roleTypeId', 'CARRIER')
+ .queryFirst()
+ assert !finAccountRole
+
+ GenericValue spuriousPartyRole = from('PartyRole')
+ .where('partyId', partyId, 'roleTypeId', 'CARRIER')
+ .queryOne()
+ assert !spuriousPartyRole
+ }
+
+ @Test
+ @Order(6)
void testUpdateFinAccountRole() {
String finAccountId = testParams.finAccountId ?: '1004'
String partyId = testParams.partyId ?: 'DEMO_COMPANY'
@@ -139,7 +169,7 @@ class AutoAcctgFinAccountTests implements JupiterTestHelper {
}
@Test
- @Order(6)
+ @Order(7)
void testDeleteFinAccountRole() {
String finAccountId = testParams.finAccountId ?: '1004'
String partyId = testParams.partyId ?: 'DEMO_COMPANY'
@@ -161,7 +191,7 @@ class AutoAcctgFinAccountTests implements JupiterTestHelper {
}
@Test
- @Order(7)
+ @Order(8)
void testCreateFinAccountTrans() {
String finAccountId = testParams.finAccountId ?: '1003'
String finAccountTransTypeId = testParams.finAccountTransTypeId ?: 'ADJUSTMENT'
@@ -180,7 +210,7 @@ class AutoAcctgFinAccountTests implements JupiterTestHelper {
}
@Test
- @Order(8)
+ @Order(9)
void testCreateFinAccountStatus() {
String finAccountId = testParams.finAccountId ?: '1003'
String statusId = testParams.statusId ?: 'FNACT_ACTIVE'
@@ -200,7 +230,7 @@ class AutoAcctgFinAccountTests implements JupiterTestHelper {
}
@Test
- @Order(9)
+ @Order(10)
void testCreateFinAccountAuth() {
String finAccountId = testParams.finAccountId ?: '1004'
String currencyUomId = testParams.currencyUomId ?: 'USD'
@@ -218,7 +248,7 @@ class AutoAcctgFinAccountTests implements JupiterTestHelper {
}
@Test
- @Order(10)
+ @Order(11)
void testSetFinAccountTransStatus() {
String finAccountTransId = testParams.finAccountTransId ?: '1010'
String statusId = testParams.statusId ?: 'FINACT_TRNS_APPROVED'
diff --git a/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgInvoiceTests.groovy b/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgInvoiceTests.groovy
index ed7c870e611..8c8878f9f51 100644
--- a/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgInvoiceTests.groovy
+++ b/applications/accounting/src/test/groovy/org/apache/ofbiz/accounting/accounting/AutoAcctgInvoiceTests.groovy
@@ -217,8 +217,31 @@ class AutoAcctgInvoiceTests implements JupiterTestHelper {
assert invoiceRole
}
+ // OFBIZ-12370: DEMO_COMPANY does not hold the CARRIER role (only INTERNAL_ORGANIZATIO/SUPPLIER,
+ // see AccountingTestsData.xml), so this combination must be rejected instead of hitting the
+ // InvoiceRole/PartyRole foreign key constraint with a raw SQL error.
@Test
@Order(11)
+ void testCreateInvoiceRoleRejectsPartyWithoutRole() {
+ Map serviceCtx = [
+ invoiceId: testParams.invoiceId ?: '1006',
+ partyId: testParams.partyId ?: 'DEMO_COMPANY',
+ roleTypeId: 'CARRIER',
+ userLogin: userLogin
+ ]
+ Map serviceResult = dispatcher.runSync('createInvoiceRole', serviceCtx)
+ assert ServiceUtil.isError(serviceResult)
+
+ GenericValue invoiceRole = from('InvoiceRole')
+ .where('invoiceId', serviceCtx.invoiceId,
+ 'partyId', serviceCtx.partyId,
+ 'roleTypeId', 'CARRIER')
+ .queryOne()
+ assert !invoiceRole
+ }
+
+ @Test
+ @Order(12)
void testCreateInvoiceTerm() {
Map serviceCtx = [
invoiceId: testParams.invoiceId ?: '1006',
@@ -239,7 +262,7 @@ class AutoAcctgInvoiceTests implements JupiterTestHelper {
}
@Test
- @Order(12)
+ @Order(13)
void testCancelInvoice() {
Map serviceCtx = [
invoiceId: testParams.invoiceId ?: '1007',
diff --git a/applications/accounting/testdef/accountingtests.xml b/applications/accounting/testdef/accountingtests.xml
index a106d6ee2b1..a2218476893 100644
--- a/applications/accounting/testdef/accountingtests.xml
+++ b/applications/accounting/testdef/accountingtests.xml
@@ -42,6 +42,9 @@
+
+
+
diff --git a/applications/order/servicedef/secas.xml b/applications/order/servicedef/secas.xml
index 29ee400cb2e..9576f05a070 100644
--- a/applications/order/servicedef/secas.xml
+++ b/applications/order/servicedef/secas.xml
@@ -515,6 +515,13 @@ under the License.
+
+
+
+
+
+
diff --git a/applications/order/src/test/groovy/org/apache/ofbiz/order/order/test/OrderRequirementTests.groovy b/applications/order/src/test/groovy/org/apache/ofbiz/order/order/test/OrderRequirementTests.groovy
index 7e73bff3f5a..17942f3a9cd 100644
--- a/applications/order/src/test/groovy/org/apache/ofbiz/order/order/test/OrderRequirementTests.groovy
+++ b/applications/order/src/test/groovy/org/apache/ofbiz/order/order/test/OrderRequirementTests.groovy
@@ -132,4 +132,37 @@ class OrderRequirementTests implements JupiterTestHelper {
assert ServiceUtil.isSuccess(serviceResult)
}
+ // createRequirementRole's in-validate eca rejects a party/role combination the party does not
+ // already hold, before it reaches ensurePartyRole (which would otherwise silently create a
+ // spurious PartyRole). DemoCustomer's seeded PartyRole set (OrderDemoData.xml) is exactly
+ // {BILL_TO_CUSTOMER, CONTACT, CUSTOMER, END_USER_CUSTOMER, PLACING_CUSTOMER, SHIP_TO_CUSTOMER} --
+ // CARRIER is provably not among them.
+ @Test
+ @Order(8)
+ void testCreateRequirementRole_rejectsRoleThePartyDoesNotHold() {
+ String requirementId = testParams.requirementId ?: '1000'
+ Map serviceCtx = [
+ requirementId: requirementId,
+ partyId: 'DemoCustomer',
+ roleTypeId: 'CARRIER',
+ userLogin: userLogin
+ ]
+ Map serviceResult = dispatcher.runSync('createRequirementRole', serviceCtx)
+ assert ServiceUtil.isError(serviceResult)
+ }
+
+ @Test
+ @Order(9)
+ void testCreateRequirementRole_allowsRoleThePartyAlreadyHolds() {
+ String requirementId = testParams.requirementId ?: '1000'
+ Map serviceCtx = [
+ requirementId: requirementId,
+ partyId: 'DemoCustomer',
+ roleTypeId: 'CUSTOMER',
+ userLogin: userLogin
+ ]
+ Map serviceResult = dispatcher.runSync('createRequirementRole', serviceCtx)
+ assert ServiceUtil.isSuccess(serviceResult)
+ }
+
}
diff --git a/applications/party/minilang/party/PartySimpleMethods.xml b/applications/party/minilang/party/PartySimpleMethods.xml
index e8af48afe79..8dddaebeae0 100644
--- a/applications/party/minilang/party/PartySimpleMethods.xml
+++ b/applications/party/minilang/party/PartySimpleMethods.xml
@@ -85,6 +85,19 @@ under the License.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/applications/party/servicedef/services.xml b/applications/party/servicedef/services.xml
index 70a03622299..2de7a6b0077 100644
--- a/applications/party/servicedef/services.xml
+++ b/applications/party/servicedef/services.xml
@@ -349,6 +349,21 @@ under the License.
+
+ Reject the request with a friendly error if the party does not already hold the given role,
+ instead of letting a role-based association be created against a party/role combination that doesn't exist.
+
+
+
+
+
+
+
+
+
+
+
Ensure that the party indicate by partyIdFrom is in the roleTypeIdFrom specifc role. If roleTypeIdFrom isn't present use _NA_
diff --git a/applications/workeffort/servicedef/secas.xml b/applications/workeffort/servicedef/secas.xml
index 80e08c3fcbf..8a711f11f15 100644
--- a/applications/workeffort/servicedef/secas.xml
+++ b/applications/workeffort/servicedef/secas.xml
@@ -79,6 +79,13 @@ under the License.
+
+
+
+
+
+
diff --git a/applications/workeffort/src/test/groovy/org/apache/ofbiz/workeffort/workeffort/test/TimesheetRoleTests.groovy b/applications/workeffort/src/test/groovy/org/apache/ofbiz/workeffort/workeffort/test/TimesheetRoleTests.groovy
new file mode 100644
index 00000000000..9d94ee00ed7
--- /dev/null
+++ b/applications/workeffort/src/test/groovy/org/apache/ofbiz/workeffort/workeffort/test/TimesheetRoleTests.groovy
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ofbiz.workeffort.workeffort.test
+
+import org.apache.ofbiz.service.ServiceUtil
+import org.apache.ofbiz.testtools.JunitJupiterTest
+import org.apache.ofbiz.testtools.JupiterTestHelper
+import org.junit.jupiter.api.Order
+import org.junit.jupiter.api.Test
+
+@JunitJupiterTest
+class TimesheetRoleTests implements JupiterTestHelper {
+
+ // createTimesheetRole's in-validate eca rejects a party/role combination the party does not
+ // already hold, before it reaches ensurePartyRole (which would otherwise silently create a
+ // spurious PartyRole). DemoCustomer's seeded PartyRole set (OrderDemoData.xml) is exactly
+ // {BILL_TO_CUSTOMER, CONTACT, CUSTOMER, END_USER_CUSTOMER, PLACING_CUSTOMER, SHIP_TO_CUSTOMER} --
+ // CARRIER is provably not among them.
+ @Test
+ @Order(1)
+ void testCreateTimesheetRole_rejectsRoleThePartyDoesNotHold() {
+ String timesheetId = testParams.timesheetId ?: 'TestTimesheet-1'
+ Map serviceCtx = [
+ timesheetId: timesheetId,
+ partyId: 'DemoCustomer',
+ roleTypeId: 'CARRIER',
+ userLogin: userLogin
+ ]
+ Map serviceResult = dispatcher.runSync('createTimesheetRole', serviceCtx)
+ assert ServiceUtil.isError(serviceResult)
+ }
+
+ @Test
+ @Order(2)
+ void testCreateTimesheetRole_allowsRoleThePartyAlreadyHolds() {
+ String timesheetId = testParams.timesheetId ?: 'TestTimesheet-1'
+ Map serviceCtx = [
+ timesheetId: timesheetId,
+ partyId: 'DemoCustomer',
+ roleTypeId: 'CUSTOMER',
+ userLogin: userLogin
+ ]
+ Map serviceResult = dispatcher.runSync('createTimesheetRole', serviceCtx)
+ assert ServiceUtil.isSuccess(serviceResult)
+ }
+
+}
diff --git a/applications/workeffort/testdef/workefforttests.xml b/applications/workeffort/testdef/workefforttests.xml
index 27e705a9ce4..df3e7dd7ae1 100644
--- a/applications/workeffort/testdef/workefforttests.xml
+++ b/applications/workeffort/testdef/workefforttests.xml
@@ -26,4 +26,7 @@
+
+
+