001// --------------------------------------------------------------------------------
002// Copyright 2002-2026 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.model.control.party.server.logic;
018
019import com.echothree.model.control.party.common.PartyRelationshipTypes;
020import com.echothree.model.control.party.common.PartyTypes;
021import com.echothree.model.control.party.common.RoleTypes;
022import com.echothree.model.control.party.common.exception.DuplicatePartyRelationshipException;
023import com.echothree.model.control.party.common.exception.NotAnEmployeeOfDepartmentsDivisionException;
024import com.echothree.model.control.party.common.exception.NotAnEmployeeOfDivisionsCompanyException;
025import com.echothree.model.control.party.common.exception.UnknownPartyRelationshipException;
026import com.echothree.model.control.party.common.exception.UnknownPartyRelationshipTypeNameException;
027import com.echothree.model.control.party.common.exception.UnknownRoleTypeNameException;
028import com.echothree.model.control.party.server.control.PartyControl;
029import com.echothree.model.control.user.server.logic.UserKeyLogic;
030import com.echothree.model.control.user.server.logic.UserSessionLogic;
031import com.echothree.model.data.party.server.entity.Party;
032import com.echothree.model.data.party.server.entity.PartyRelationship;
033import com.echothree.model.data.party.server.entity.PartyRelationshipType;
034import com.echothree.model.data.party.server.entity.RoleType;
035import com.echothree.util.common.message.ExecutionErrors;
036import com.echothree.util.common.persistence.BasePK;
037import com.echothree.util.server.control.BaseLogic;
038import com.echothree.util.server.message.ExecutionErrorAccumulator;
039import javax.enterprise.context.ApplicationScoped;
040import javax.enterprise.inject.spi.CDI;
041import javax.inject.Inject;
042
043@ApplicationScoped
044public class PartyRelationshipLogic
045        extends BaseLogic {
046
047    @Inject
048    PartyControl partyControl;
049
050    @Inject
051    PartyLogic partyLogic;
052
053    @Inject
054    UserKeyLogic userKeyLogic;
055
056    @Inject
057    UserSessionLogic userSessionLogic;
058
059    protected PartyRelationshipLogic() {
060        super();
061    }
062
063    public static PartyRelationshipLogic getInstance() {
064        return CDI.current().select(PartyRelationshipLogic.class).get();
065    }
066
067    public PartyRelationshipType getPartyRelationshipTypeByName(final ExecutionErrorAccumulator eea, final String partyRelationshipTypeName) {
068        var partyRelationshipType = partyControl.getPartyRelationshipTypeByName(partyRelationshipTypeName);
069
070        if(partyRelationshipType == null) {
071            handleExecutionError(UnknownPartyRelationshipTypeNameException.class, eea, ExecutionErrors.UnknownPartyRelationshipTypeName.name(), partyRelationshipTypeName);
072        }
073
074        return partyRelationshipType;
075    }
076
077    public RoleType getRoleTypeByName(final ExecutionErrorAccumulator eea, final String roleTypeName) {
078        var roleType = partyControl.getRoleTypeByName(roleTypeName);
079
080        if(roleType == null) {
081            handleExecutionError(UnknownRoleTypeNameException.class, eea, ExecutionErrors.UnknownRoleTypeName.name(), roleTypeName);
082        }
083
084        return roleType;
085    }
086
087    public PartyRelationship createPartyRelationship(final ExecutionErrorAccumulator eea, final PartyRelationshipType partyRelationshipType,
088            final Party fromParty, final RoleType fromRoleType, final Party toParty, final RoleType toRoleType, final BasePK createdBy) {
089        var partyRelationship = partyControl.getPartyRelationship(partyRelationshipType, fromParty, fromRoleType, toParty, toRoleType);
090
091        if(partyRelationship == null) {
092            partyControl.createPartyRelationship(partyRelationshipType, fromParty, fromRoleType, toParty, toRoleType, createdBy);
093        } else {
094            handleExecutionError(DuplicatePartyRelationshipException.class, eea, ExecutionErrors.DuplicatePartyRelationship.name(),
095                    partyRelationshipType.getPartyRelationshipTypeName(),
096                    fromParty.getLastDetail().getPartyName(), fromRoleType.getRoleTypeName(),
097                    toParty.getLastDetail().getPartyName(), toRoleType.getRoleTypeName());
098        }
099
100        return partyRelationship;
101    }
102
103    public void deletePartyRelationship(final ExecutionErrorAccumulator eea, final PartyRelationshipType partyRelationshipType, final Party fromParty,
104            final RoleType fromRoleType, final Party toParty, final RoleType toRoleType, final BasePK deletedBy) {
105        var partyRelationship = partyControl.getPartyRelationshipForUpdate(partyRelationshipType, fromParty, fromRoleType, toParty, toRoleType);
106
107        if(partyRelationship == null) {
108            handleExecutionError(UnknownPartyRelationshipException.class, eea, ExecutionErrors.UnknownPartyRelationship.name(),
109                    partyRelationshipType.getPartyRelationshipTypeName(),
110                    fromParty.getLastDetail().getPartyName(), fromRoleType.getRoleTypeName(),
111                    toParty.getLastDetail().getPartyName(), toRoleType.getRoleTypeName());
112        } else {
113            userKeyLogic.clearUserKeysByPartyRelationship(partyRelationship);
114            userSessionLogic.deleteUserSessionsByPartyRelationship(partyRelationship);
115            partyControl.deletePartyRelationship(partyRelationship, deletedBy);
116        }
117    }
118
119    public boolean isEmployeeOfCompany(final ExecutionErrorAccumulator eea, final Party company, final Party employee) {
120        var result = false;
121
122        partyLogic.checkPartyType(eea, company, PartyTypes.COMPANY.name());
123        partyLogic.checkPartyType(eea, employee, PartyTypes.EMPLOYEE.name());
124
125        if(!hasExecutionErrors(eea)) {
126            result = partyControl.countPartyRelationships(getPartyRelationshipTypeByName(eea, PartyRelationshipTypes.EMPLOYMENT.name()),
127                    company, getRoleTypeByName(eea, RoleTypes.EMPLOYER.name()),
128                    employee, getRoleTypeByName(eea, PartyTypes.EMPLOYEE.name())) == 1;
129        }
130
131        return result;
132    }
133
134    public PartyRelationship addEmployeeToCompany(final ExecutionErrorAccumulator eea, final Party companyParty, final Party employeeParty, final BasePK createdBy) {
135        PartyRelationship partyRelationship = null;
136
137        partyLogic.checkPartyType(eea, employeeParty, PartyTypes.EMPLOYEE.name());
138        partyLogic.checkPartyType(eea, companyParty, PartyTypes.COMPANY.name());
139
140        if(!hasExecutionErrors(eea)) {
141            partyRelationship = createPartyRelationship(eea, getPartyRelationshipTypeByName(eea, PartyRelationshipTypes.EMPLOYMENT.name()),
142                        companyParty, getRoleTypeByName(eea, RoleTypes.EMPLOYER.name()),
143                        employeeParty, getRoleTypeByName(eea, PartyTypes.EMPLOYEE.name()), createdBy);
144        }
145
146        return partyRelationship;
147    }
148
149    public void removeEmployeeFromCompany(final ExecutionErrorAccumulator eea, final Party companyParty, final Party employeeParty, final BasePK deletedBy) {
150        partyLogic.checkPartyType(eea, employeeParty, PartyTypes.EMPLOYEE.name());
151        partyLogic.checkPartyType(eea, companyParty, PartyTypes.COMPANY.name());
152
153        if(!hasExecutionErrors(eea)) {
154            removeEmployeeFromCompanysDivisions(eea, companyParty, employeeParty, deletedBy);
155
156            deletePartyRelationship(eea, getPartyRelationshipTypeByName(eea, PartyRelationshipTypes.EMPLOYMENT.name()),
157                        companyParty, getRoleTypeByName(eea, RoleTypes.EMPLOYER.name()),
158                        employeeParty, getRoleTypeByName(eea, PartyTypes.EMPLOYEE.name()), deletedBy);
159        }
160    }
161
162    public boolean isEmployeeOfDivision(final ExecutionErrorAccumulator eea, final Party division, final Party employee) {
163        var result = false;
164
165        partyLogic.checkPartyType(eea, division, PartyTypes.DIVISION.name());
166        partyLogic.checkPartyType(eea, employee, PartyTypes.EMPLOYEE.name());
167
168        if(!hasExecutionErrors(eea)) {
169            result = partyControl.countPartyRelationships(getPartyRelationshipTypeByName(eea, PartyRelationshipTypes.EMPLOYMENT.name()),
170                    division, getRoleTypeByName(eea, RoleTypes.EMPLOYER.name()),
171                    employee, getRoleTypeByName(eea, PartyTypes.EMPLOYEE.name())) == 1;
172        }
173
174        return result;
175    }
176
177    public PartyRelationship addEmployeeToDivision(final ExecutionErrorAccumulator eea, final Party divisionParty, final Party employeeParty, final BasePK createdBy) {
178        PartyRelationship partyRelationship = null;
179
180        partyLogic.checkPartyType(eea, employeeParty, PartyTypes.EMPLOYEE.name());
181        partyLogic.checkPartyType(eea, divisionParty, PartyTypes.DIVISION.name());
182
183        if(!hasExecutionErrors(eea)) {
184            var partyDivision = partyControl.getPartyDivision(divisionParty);
185            var partyCompany = partyControl.getPartyCompany(partyDivision.getCompanyParty());
186            var companyParty = partyCompany.getParty();
187
188            if(isEmployeeOfCompany(eea, companyParty, employeeParty)) {
189                partyRelationship = createPartyRelationship(eea, getPartyRelationshipTypeByName(eea, PartyRelationshipTypes.EMPLOYMENT.name()),
190                            divisionParty, getRoleTypeByName(eea, RoleTypes.EMPLOYER.name()),
191                            employeeParty, getRoleTypeByName(eea, PartyTypes.EMPLOYEE.name()), createdBy);
192            } else {
193                handleExecutionError(NotAnEmployeeOfDivisionsCompanyException.class, eea, ExecutionErrors.NotAnEmployeeOfDivisionsCompany.name(),
194                        partyCompany.getPartyCompanyName(), employeeParty.getLastDetail().getPartyName());
195            }
196        }
197
198        return partyRelationship;
199    }
200
201    public void removeEmployeeFromCompanysDivisions(final ExecutionErrorAccumulator eea, final Party companyParty, final Party employeeParty, final BasePK deletedBy) {
202        var partyDivisions = partyControl.getDivisionsByCompany(companyParty);
203
204        partyDivisions.stream().map((partyDivision) -> partyDivision.getParty()).filter((divisionParty) -> isEmployeeOfDivision(eea, divisionParty, employeeParty)).forEach((divisionParty) -> {
205            removeEmployeeFromDivisionsDepartments(eea, employeeParty, employeeParty, deletedBy);
206            removeEmployeeFromDivision(eea, divisionParty, employeeParty, deletedBy);
207        });
208    }
209
210    public void removeEmployeeFromDivision(final ExecutionErrorAccumulator eea, final Party divisionParty, final Party employeeParty, final BasePK deletedBy) {
211        partyLogic.checkPartyType(eea, employeeParty, PartyTypes.EMPLOYEE.name());
212        partyLogic.checkPartyType(eea, divisionParty, PartyTypes.DIVISION.name());
213
214        if(!hasExecutionErrors(eea)) {
215            removeEmployeeFromDivisionsDepartments(eea, divisionParty, employeeParty, deletedBy);
216
217            deletePartyRelationship(eea, getPartyRelationshipTypeByName(eea, PartyRelationshipTypes.EMPLOYMENT.name()),
218                        divisionParty, getRoleTypeByName(eea, RoleTypes.EMPLOYER.name()),
219                        employeeParty, getRoleTypeByName(eea, PartyTypes.EMPLOYEE.name()), deletedBy);
220        }
221    }
222
223    public boolean isEmployeeOfDepartment(final ExecutionErrorAccumulator eea, final Party department, final Party employee) {
224        var result = false;
225
226        partyLogic.checkPartyType(eea, department, PartyTypes.DEPARTMENT.name());
227        partyLogic.checkPartyType(eea, employee, PartyTypes.EMPLOYEE.name());
228
229        if(!hasExecutionErrors(eea)) {
230            result = partyControl.countPartyRelationships(getPartyRelationshipTypeByName(eea, PartyRelationshipTypes.EMPLOYMENT.name()),
231                    department, getRoleTypeByName(eea, RoleTypes.EMPLOYER.name()),
232                    employee, getRoleTypeByName(eea, PartyTypes.EMPLOYEE.name())) == 1;
233        }
234
235        return result;
236    }
237
238    public PartyRelationship addEmployeeToDepartment(final ExecutionErrorAccumulator eea, final Party departmentParty, final Party employeeParty, final BasePK createdBy) {
239        PartyRelationship partyRelationship = null;
240
241        partyLogic.checkPartyType(eea, employeeParty, PartyTypes.EMPLOYEE.name());
242        partyLogic.checkPartyType(eea, departmentParty, PartyTypes.DEPARTMENT.name());
243
244        if(!hasExecutionErrors(eea)) {
245            var partyDepartment = partyControl.getPartyDepartment(departmentParty);
246            var partyDivision = partyControl.getPartyDivision(partyDepartment.getDivisionParty());
247            var partyCompany = partyControl.getPartyCompany(partyDivision.getCompanyParty());
248            var divisionParty = partyDivision.getParty();
249
250            if(isEmployeeOfDivision(eea, divisionParty, employeeParty)) {
251                partyRelationship = createPartyRelationship(eea, getPartyRelationshipTypeByName(eea, PartyRelationshipTypes.EMPLOYMENT.name()),
252                            departmentParty, getRoleTypeByName(eea, RoleTypes.EMPLOYER.name()),
253                            employeeParty, getRoleTypeByName(eea, PartyTypes.EMPLOYEE.name()), createdBy);
254            } else {
255                handleExecutionError(NotAnEmployeeOfDepartmentsDivisionException.class, eea, ExecutionErrors.NotAnEmployeeOfDepartmentsDivision.name(),
256                        partyCompany.getPartyCompanyName(), partyDivision.getPartyDivisionName(), employeeParty.getLastDetail().getPartyName());
257            }
258        }
259
260        return partyRelationship;
261    }
262
263    public void removeEmployeeFromDivisionsDepartments(final ExecutionErrorAccumulator eea, final Party divisionParty, final Party employeeParty, final BasePK deletedBy) {
264        var partyDepartments = partyControl.getDepartmentsByDivision(divisionParty);
265
266        partyDepartments.stream().map((partyDepartment) -> partyDepartment.getParty()).filter((departmentParty) -> isEmployeeOfDepartment(eea, departmentParty, employeeParty)).forEach((departmentParty) -> {
267            removeEmployeeFromDepartment(eea, departmentParty, employeeParty, deletedBy);
268        });
269    }
270
271    public void removeEmployeeFromDepartment(final ExecutionErrorAccumulator eea, final Party departmentParty, final Party employeeParty, final BasePK deletedBy) {
272        partyLogic.checkPartyType(eea, employeeParty, PartyTypes.EMPLOYEE.name());
273        partyLogic.checkPartyType(eea, departmentParty, PartyTypes.DEPARTMENT.name());
274
275        if(!hasExecutionErrors(eea)) {
276            deletePartyRelationship(eea, getPartyRelationshipTypeByName(eea, PartyRelationshipTypes.EMPLOYMENT.name()),
277                        departmentParty, getRoleTypeByName(eea, RoleTypes.EMPLOYER.name()),
278                        employeeParty, getRoleTypeByName(eea, PartyTypes.EMPLOYEE.name()), deletedBy);
279        }
280    }
281
282}