<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->

<simple-methods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://ofbiz.apache.org/Simple-Method" xsi:schemaLocation="http://ofbiz.apache.org/Simple-Method http://ofbiz.apache.org/dtds/simple-methods.xsd">

    <!-- Timesheet Services -->
    <simple-method method-name="createTimesheet" short-description="Create Timesheet">
        <make-value entity-name="Timesheet" value-field="newEntity"/>
        <sequenced-id sequence-name="Timesheet" field="newEntity.timesheetId"/>
        <field-to-result field="newEntity.timesheetId" result-name="timesheetId"/>

        <set-nonpk-fields map="parameters" value-field="newEntity"/>
        <if-empty field="newEntity.statusId">
            <set field="newEntity.statusId" value="TIMESHEET_IN_PROCESS"/>
        </if-empty>
        <create-value value-field="newEntity"/>
    </simple-method>
    <simple-method method-name="updateTimesheet" short-description="Update Timesheet">
        <call-simple-method method-name="checkTimesheetStatus"/>
        <set-nonpk-fields map="parameters" value-field="timesheet"/>
        <store-value value-field="timesheet"/>
    </simple-method>
    <simple-method method-name="updateTimesheetToInProcess" short-description="Updates the Timesheet status back to in process to be able to correct errors">
        <entity-one entity-name="Timesheet" value-field="timesheet"/>
        <set field="timesheet.statusId" value="TIMESHEET_IN_PROCESS"/>
        <store-value value-field="timesheet"/>
    </simple-method>
    <simple-method method-name="deleteTimesheet" short-description="Delete Timesheet">
        <call-simple-method method-name="checkTimesheetStatus"/>
        <remove-value value-field="timesheet"/>
    </simple-method>
    <simple-method method-name="createTimesheetForThisWeek" short-description="Create Timesheet For This Week of no date provided, otherwise for a specific week">
        <if-empty field="parameters.requiredDate">
            <now-timestamp field="nowTimestamp"/>
            <else>
                <set field="nowTimestamp" from-field="parameters.requiredDate"/>
            </else>
        </if-empty>
        <call-class-method class-name="org.apache.ofbiz.base.util.UtilDateTime" method-name="getWeekStart" ret-field="parameters.fromDate">
            <field field="nowTimestamp" type="java.sql.Timestamp"/>
        </call-class-method>
        <call-class-method class-name="org.apache.ofbiz.base.util.UtilDateTime" method-name="getWeekEnd" ret-field="parameters.thruDate">
            <field field="nowTimestamp" type="java.sql.Timestamp"/>
        </call-class-method>
        <!-- check if timesheet already exists -->
        <entity-and entity-name="Timesheet" list="timesheets">
            <field-map field-name="partyId" from-field="parameters.partyId"/>
            <field-map field-name="fromDate" from-field="parameters.fromDate"/>
            <field-map field-name="thruDate" from-field="parameters.thruDate"/>
        </entity-and>
        <if-empty field="timesheets">
                <call-simple-method method-name="createTimesheet"/>
            <else>
                <add-error>
                    <fail-property resource="WorkEffortUiLabels" property="WorkEffortTimesheetAlreadyExists"/>
                </add-error>
                <check-errors/>
            </else>
        </if-empty>
    </simple-method>
    <simple-method  method-name="createTimesheets" short-description="Creates Timesheet multiple parties at a time">
       <iterate list="parameters.partyIdList" entry="partyId">
           <set field="parameters.partyId" from-field="partyId"/>
           <set-service-fields service-name="createTimesheet" map="parameters" to-map="createParams"/>
           <call-service service-name="createTimesheet" in-map-name="createParams"/>
       </iterate>
    </simple-method>
    <simple-method method-name="addTimesheetToInvoice" short-description="Add Timesheet to Invoice">
        <entity-one entity-name="Timesheet" value-field="timesheet"/>
        <get-related value-field="timesheet" relation-name="TimeEntry" list="timeEntryList"/>

        <if-empty field="parameters.invoiceId">
            <set-service-fields service-name="createInvoice" map="parameters" to-map="createInvoiceMap"/>
            <set field="createInvoiceMap.invoiceTypeId" value="SALES_INVOICE"/>
            <set field="createInvoiceMap.statusId" value="INVOICE_IN_PROCESS"/>
            <call-service service-name="createInvoice" in-map-name="createInvoiceMap">
                <result-to-field result-name="invoiceId"/>
            </call-service>
            <field-to-result field="invoiceId"/>
        <else>
            <set field="invoiceId" from-field="parameters.invoiceId"/>
        </else>
        </if-empty>

        <entity-one entity-name="Invoice" value-field="invoice"/>
        <if-empty field="invoice">
            <add-error>
                <fail-property resource="WorkEffortUiLabels" property="WorkEffortTimesheetCannotFindInvoice"/>
            </add-error>
            <check-errors/>
        </if-empty>

        <call-simple-method method-name="createTimeEntryInvoiceItemsInline"/>
    </simple-method>
    <simple-method method-name="addWorkEffortTimeToInvoice" short-description="Add Work Effort Time to Invoice">
        <entity-one entity-name="WorkEffort" value-field="workEffort"/>
        <if-empty field="parameters.invoiceId">
            <set-service-fields service-name="createInvoice" map="parameters" to-map="createInvoiceMap"/>
            <set field="createInvoiceMap.invoiceTypeId" value="SALES_INVOICE"/>
            <set field="createInvoiceMap.statusId" value="INVOICE_IN_PROCESS"/>
            <call-service service-name="createInvoice" in-map-name="createInvoiceMap">
                <result-to-field result-name="invoiceId" field="parameters.invoiceId"/>
            </call-service>
            <field-to-result field="parameters.invoiceId" result-name="invoiceId"/>
        </if-empty>
        <entity-one entity-name="Invoice" value-field="invoice"/>
        <if-empty field="invoice">
            <add-error>
                <fail-property resource="WorkEffortUiLabels" property="WorkEffortTimesheetCannotFindInvoice"/>
            </add-error>
            <check-errors/>
        </if-empty>
        <!-- update currency, can be changed with regenerate -->
        <!-- find the currency of the receiving party -->
        <entity-one entity-name="Party" value-field="party">
            <field-map field-name="partyId" value="${invoice.partyId}"/>
        </entity-one>
        <if-empty field="party.preferredCurrencyUomId">
            <property-to-field resource="general" property="currency.uom.id.default" field="party.preferredCurrencyUomId"/>
        </if-empty>
        <set field="updateInvoiceMap.invoiceId" from-field="parameters.invoiceId"/>
        <set field="updateInvoiceMap.currencyUomId" from-field="party.preferredCurrencyUomId"/>
        <now-timestamp field="updateInvoiceMap.invoiceDate"/>
        <if-empty field="updateInvoiceMap.currencyUomId">
            <property-to-field field="invoice.currencyUomId" resource="general" property="currency.uom.id.default" default="USD"/>
        </if-empty>
        <call-service service-name="updateInvoice" in-map-name="updateInvoiceMap"/>
        <entity-one entity-name="Invoice" value-field="invoice"/>
        <call-simple-method method-name="createTimeEntryInvoiceItemsInline"/>
    </simple-method>
    <simple-method method-name="createTimeEntryInvoiceItemsInline" short-description="createTimeEntryInvoiceItemsInline">
        <set field="orderBy[]" value="rateTypeId"/>
        <set field="invoiceItemMap.invoiceId" from-field="parameters.invoiceId"/>
        <set field="invoiceItemMap.taxableFlag" value="N"/>
        <set field="invoiceItemMap.invoiceItemTypeId" value="INV_TE_ITEM"/>
        <set field="invoiceItemMap.uomId" value="TF_hr"/>
        <if-not-empty field="timesheet">
            <set field="invoiceItemMap.description" value="[Timesheet:${timesheet.timesheetId}]"/>
        </if-not-empty>
        <if-not-empty field="workEffort">
            <get-related value-field="workEffort" relation-name="TimeEntry" list="timeEntryList" order-by-list="orderBy"/>
            <set field="invoiceItemMap.description" value="${workEffort.workEffortName} [Task:${workEffort.workEffortId}]"/>
            <get-related value-field="workEffort" relation-name="CustRequestWorkEffort" list="custRequestWorkEfforts"/>
            <if-not-empty field="custRequestWorkEfforts">
                <first-from-list list="custRequestWorkEfforts" entry="custRequestWorkEffort"/>
                <get-related-one value-field="custRequestWorkEffort" relation-name="CustRequest" to-value-field="custRequest"/>
                <if-not-empty field="custRequest">
                    <set field="invoiceItemDescription" value="${custRequest.custRequestName} [CRQ:${custRequest.custRequestId}] ${custRequest.description}"/>
                    <set field="invoiceItemMap.description" value="${groovy:invoiceItemDescription.size()&gt;255?invoiceItemDescription.substring(0,251)+&quot; ...&quot;:invoiceItemDescription}"/>
                </if-not-empty>
            </if-not-empty>
        </if-not-empty>
        <iterate list="timeEntryList" entry="timeEntry">
            <if>
                <condition>
                    <or>
                        <and>
                            <not><if-empty field="parameters.thruDate"/></not>
                            <if-compare-field operator="less" field="timeEntry.fromDate" to-field="parameters.thruDate"/>
                        </and>
                        <if-empty field="parameters.thruDate"/>
                    </or>
                </condition>
                <then>
                    <!-- check invoice -->
                    <if-compare field="invoice.statusId" operator="equals" value="INVOICE_IN_PROCESS">
                        <!-- only add to invoice if it is not already on an invoice-->
                        <if-empty field="timeEntry.invoiceId">
                            <!-- check if only a percentage of the hours need to be used -->
                            <if-empty field="timeEntry.partyId">
                                <if-not-empty field="timeEntry.timesheetId">
                                    <entity-one entity-name="Timesheet" value-field="timesheet">
                                        <field-map field-name="timesheetId" from-field="timeEntry.timesheetId"/>
                                    </entity-one>
                                    <set field="timeEntry.partyId" from-field="timesheet.partyId"/>
                                </if-not-empty>
                            </if-empty>
                            <if-not-empty field="timeEntry.partyId">
                                <entity-and entity-name="PartyRate" list="partyRates" filter-by-date="true">
                                    <field-map field-name="rateTypeId" from-field="timeEntry.rateTypeId"/>
                                    <field-map field-name="partyId" from-field="timeEntry.partyId"/>
                                </entity-and>
                                <if-not-empty field="partyRates">
                                    <first-from-list list="partyRates" entry="partyRate"/>
                                    <if-not-empty field="partyRate.percentageUsed">
                                        <calculate field="timeEntry.hours" type="Double">
                                            <calcop operator="multiply" field="timeEntry.hours">
                                                <calcop operator="get" field="partyRate.percentageUsed"/>
                                            </calcop>
                                        </calculate>
                                        <calculate field="timeEntry.hours" type="Double">
                                            <calcop operator="divide" field="timeEntry.hours">
                                                <number value="100"/>
                                            </calcop>
                                        </calculate>
                                    </if-not-empty>
                                </if-not-empty>
                            </if-not-empty>
                            <set field="getTimeEntryRate.timeEntryId" from-field="timeEntry.timeEntryId"/>
                            <set field="getTimeEntryRate.currencyUomId" from-field="invoice.currencyUomId"/>
                            <call-service service-name="getTimeEntryRate" in-map-name="getTimeEntryRate">
                                <result-to-field result-name="rateAmount"/>
                            </call-service>
                            <!--  check if the RateTypeId changed or the first time entry record and invoice item is not exist with the same amount and description-->
                            <entity-and entity-name="InvoiceItem" list="existAmountAndDescriptionInvoiceItems">
                                <field-map field-name="invoiceId" from-field="invoiceItemMap.invoiceId"/>
                                <field-map field-name="amount" from-field="rateAmount"/>
                                <field-map field-name="description" from-field="invoiceItemMap.description"/>
                            </entity-and>
                            <set field="invoiceItemMap.invoiceItemSeqId" from-field="existAmountAndDescriptionInvoiceItems[0].invoiceItemSeqId"/>
                            <if>
                                <condition>
                                    <and>
                                        <or>
                                            <if-empty field="oldRateAmount"/>
                                            <if-compare-field field="rateAmount" operator="not-equals" to-field="oldRateAmount"/>
                                        </or>
                                        <if-empty field="existAmountAndDescriptionInvoiceItems"/>
                                    </and>
                                </condition>
                                <then>
                                    <set field="invoiceItemMap.amount" from-field="rateAmount" default-value="0.0" type="BigDecimal"/>
                                    <if-compare field="parameters.combineInvoiceItem" operator="equals" value="Y" >
                                        <set field="invoiceItemMap.quantity" from-field="timeEntry.hours" default-value="0.0" type="BigDecimal"/>
                                        <clear-field field="invoiceItemMap.invoiceItemSeqId"/><!-- make sure a new one is created -->
                                        <call-service service-name="createInvoiceItem" in-map-name="invoiceItemMap">
                                            <result-to-field result-name="invoiceItemSeqId" field="invoiceItemMap.invoiceItemSeqId"/>
                                        </call-service>
                                    </if-compare>
                                </then>
                                <else>
                                    <if-compare field="parameters.combineInvoiceItem" operator="equals" value="Y" >
                                        <!-- or combine them when it is the same rate, amount and description-->
                                        <set field="invoiceItemMap.quantity" from-field="existAmountAndDescriptionInvoiceItems[0].quantity"/>
                                        <calculate field="invoiceItemMap.quantity">
                                            <calcop operator="get" field="invoiceItemMap.quantity"/>
                                            <calcop operator="add" field="timeEntry.hours"/>
                                        </calculate>
                                        <call-service service-name="updateInvoiceItem" in-map-name="invoiceItemMap"/>
                                    </if-compare>
                                </else>
                            </if>
                            <set field="oldRateAmount" from-field="rateAmount"/>

                            <!-- create an invoiceitem for every time entry -->
                            <if-compare field="parameters.combineInvoiceItem" operator="not-equals" value="Y" >
                                <set field="invoiceItemMap.description" from-field="timeEntry.comments"/>
                                <if-empty field="invoiceItemMap.description">
                                    <set field="invoiceItemMap.description" from-field="workEffort.workEffortName"/>
                                </if-empty>
                                <set field="invoiceItemMap.quantity" from-field="timeEntry.hours" default-value="0.0" type="BigDecimal"/>
                                <clear-field field="invoiceItemMap.invoiceItemSeqId"/><!-- make sure a new one is created -->
                                <call-service service-name="createInvoiceItem" in-map-name="invoiceItemMap">
                                    <result-to-field result-name="invoiceItemSeqId" field="invoiceItemMap.invoiceItemSeqId"/>
                                </call-service>
                            </if-compare>
                            <!--  update the time entry -->
                            <set field="updTimeEntry.timeEntryId" from-field="timeEntry.timeEntryId"/>
                            <set field="updTimeEntry.invoiceId" from-field="invoiceItemMap.invoiceId"/>
                            <set field="updTimeEntry.invoiceItemSeqId" from-field="invoiceItemMap.invoiceItemSeqId"/>
                            <call-service service-name="updateTimeEntry" in-map-name="updTimeEntry"/>
                            <!-- else>
                                <log level="info" message="Timeentry: ${timeEntry.timeEntryId} already connected to invoiceId: ${timeEntry.invoiceId}...not added to invoiceItem"/>
                            </else-->
                        </if-empty>
                        <else>
                            <set field="errMsg" value="Invoice ${invoiceId} should have the status 'in progress', the status is however: ${invoice.statusId}"/>
                            <log level="error" message="${errMsg}"/>
                            <add-error error-list-name="errorMessageList">
                                <fail-property resource="WorkEffortUiLabels" property="WorkEffortTimesheetInvoiceShuoldBeInProgressStatus"/>
                            </add-error>
                            <return response-code="error"/>
                        </else>
                    </if-compare>
                </then>
            </if>
        </iterate>
    </simple-method>

    <!-- TimesheetRole Services -->
    <simple-method method-name="createTimesheetRole" short-description="Create TimesheetRole">
        <set field="ensurePartyRoleCtx.partyId" from-field="parameters.partyId"/>
        <set field="ensurePartyRoleCtx.roleTypeId" from-field="parameters.roleTypeId"/>
        <call-service service-name="ensurePartyRole" in-map-name="ensurePartyRoleCtx"/>
        <make-value entity-name="TimesheetRole" value-field="newEntity"/>
        <set-pk-fields map="parameters" value-field="newEntity"/>
        <create-value value-field="newEntity"/>
    </simple-method>
    <simple-method method-name="deleteTimesheetRole" short-description="Delete TimesheetRole">
        <entity-one entity-name="TimesheetRole" value-field="lookedUpValue"/>
        <remove-value value-field="lookedUpValue"/>
    </simple-method>

    <!-- TimeEntry Services -->
    <simple-method method-name="createTimeEntry" short-description="Create TimeEntry">
        <call-simple-method method-name="checkTimesheetStatus"/>
        <now-timestamp field="nowTimestamp"/>

        <make-value entity-name="TimeEntry" value-field="newEntity"/>
        <sequenced-id sequence-name="TimeEntry" field="newEntity.timeEntryId"/>
        <field-to-result field="newEntity.timeEntryId" result-name="timeEntryId"/>

        <set-nonpk-fields map="parameters" value-field="newEntity"/>
        <if-empty field="newEntity.fromDate">
            <call-class-method class-name="org.apache.ofbiz.base.util.UtilDateTime" method-name="getDayStart" ret-field="newEntity.fromDate">
                <field field="nowTimestamp" type="java.sql.Timestamp"/>
            </call-class-method>
        </if-empty>
        <create-value value-field="newEntity"/>
    </simple-method>
    <simple-method method-name="updateTimeEntry" short-description="Update TimeEntry">
        <entity-one entity-name="TimeEntry" value-field="lookedUpValue"/>
        <call-simple-method method-name="checkTimesheetStatus"/>
        
        <!-- update all fields except invoiceId,invoiceItemSeqId and only those if they are not yet set -->
        <map-to-map map="parameters" to-map="fieldsToCopy"/>
        <clear-field field="fieldsToCopy.invoiceId"/>
        <clear-field field="fieldsToCopy.invoiceItemSeqId"/>
        
        <!-- if dates are updated and no hours value is specified, calc it here -->
        <if>
            <condition>
                <and>
                    <if-empty field="fieldsToCopy.hours"/>
                    <or>
                        <not><if-empty field="fieldsToCopy.fromDate"/></not>
                        <not><if-empty field="fieldsToCopy.thruDate"/></not>
                    </or>
                    <or>
                        <if-compare-field field="fieldsToCopy.fromDate" operator="not-equals" to-field="lookedUpValue.fromDate"/>
                        <if-compare-field field="fieldsToCopy.thruDate" operator="not-equals" to-field="lookedUpValue.thruDate"/>
                    </or>
                </and>
            </condition>
            <then>
                <set field="fieldsToCopy.hours" value="${groovy:org.apache.ofbiz.base.util.UtilDateTime.getInterval((fieldsToCopy.fromDate ? fieldsToCopy.fromDate : lookedUpValue.fromDate), (fieldsToCopy.thruDate ? fieldsToCopy.thruDate : lookedUpValue.thruDate))/3600000}" type="Double"/>
            </then>
        </if>
        
        <set-nonpk-fields map="fieldsToCopy" value-field="lookedUpValue"/>
        
        <if-not-empty field="parameters.invoiceId">
            <if-empty field="lookedUpValue.invoiceId">
                <set field="lookedUpValue.invoiceId" from-field="parameters.invoiceId"/>
                <set field="lookedUpValue.invoiceItemSeqId" from-field="parameters.invoiceItemSeqId"/>
            </if-empty>
        </if-not-empty>
        
        <store-value value-field="lookedUpValue"/>
    </simple-method>
    <simple-method method-name="deleteTimeEntry" short-description="Delete TimeEntry">
        <entity-one entity-name="TimeEntry" value-field="lookedUpValue"/>
        <call-simple-method method-name="checkTimesheetStatus"/>
        <remove-value value-field="lookedUpValue"/>
    </simple-method>
    <simple-method method-name="unlinkInvoiceFromTimeEntry" short-description="Delete TimeEntry">
        <entity-one entity-name="TimeEntry" value-field="lookedUpValue"/>
        <field-to-result field="lookedUpValue.invoiceId" result-name="invoiceId"/>
        <clear-field field="lookedUpValue.invoiceId"/>
        <store-value value-field="lookedUpValue"/>
    </simple-method>
    <simple-method method-name="getTimeEntryRate" short-description="Get TimeEntry Rate">
        <entity-one entity-name="TimeEntry" value-field="timeEntry"/>

        <set-service-fields service-name="getRateAmount" map="parameters" to-map="getRate"/>
        <set field="getRate.rateCurrencyUomId" from-field="parameters.currencyUomId"/>
        <set field="getRate.rateTypeId" from-field="timeEntry.rateTypeId"/>
        <if-empty field="timeEntry.partyId">
            <get-related-one value-field="timeEntry" relation-name="Timesheet" to-value-field="timesheet"/>
            <if-not-empty field="timesheet">
                <set field="getRate.partyId" from-field="timesheet.partyId"/>
            </if-not-empty>
            <else>
                <set field="getRate.partyId" from-field="timeEntry.partyId"/>
            </else>
        </if-empty>
        <call-service service-name="getRateAmount" in-map-name="getRate">
            <result-to-result result-name="rateAmount"/>
        </call-service>
    </simple-method>

    <!-- subroutines -->
    <simple-method method-name="checkTimesheetStatus" short-description="Check access and if the timesheet is in progress, however do allow invoiceId to be updated when completed (need to invoice completed timesheets)">
        <if>
            <condition>
                <and>
                    <or>
                        <not><if-empty field="parameters.timesheetId"/></not>
                        <not><if-empty field="lookedUpValue.timesheetId"/></not>
                    </or>
                    <if-empty field="parameters.invoiceId"/>
                </and>
            </condition>
            <then>
                <if-empty field="parameters.timesheetId">
                    <set field="parameters.timesheetId" from-field="lookedUpValue.timesheetId"/>
                </if-empty>
                <entity-one entity-name="Timesheet" value-field="timesheet"/>
                <if-empty field="timesheet">
                    <add-error>
                        <fail-property resource="WorkEffortUiLabels" property="WorkEffortTimesheetCouldNotBeFound"/>
                    </add-error>
                    <log level="info" message="Timesheet not found, timesheet: ${parameters.timesheetId}"/>
                    <check-errors/>
                </if-empty>
                <!-- check if the status is in progress... -->
                <if-compare field="timesheet.statusId" value="TIMESHEET_IN_PROCESS" operator="not-equals">
                    <get-related-one value-field="timesheet" relation-name="StatusItem" to-value-field="statusItem"/>
                    <add-error>
                        <fail-property resource="WorkEffortUiLabels" property="WorkEffortTimesheetNotInProcessStatus"/>
                    </add-error>
                    <log level="info" message="Can only update Timesheet, when status is in-process...is now: ${timesheet.statusId}"/>
                </if-compare>
            </then>
        </if>
        <check-errors/>
    </simple-method>

</simple-methods>
