Compare commits

..

1 Commits

Author SHA1 Message Date
Patrick Fic
bf75ad68f9 Merged master into development 2021-05-20 14:57:30 +00:00
763 changed files with 57550 additions and 118552 deletions

1
.gitignore vendored
View File

@@ -112,4 +112,3 @@ firebase/.env
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
logs/oAuthClient-log.log

View File

@@ -1 +1 @@
client_max_body_size 50M;
client_max_body_size 15M;

File diff suppressed because it is too large Load Diff

View File

@@ -1,586 +0,0 @@
<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<xsl:for-each select="//TranslatedData">
<xsl:element name="TranslatedData">
<xsl:attribute name="TranslationOutputFile">
<xsl:value-of select="@TranslationOutputFile"/>
</xsl:attribute>
<xsl:choose>
<xsl:when test="Details/@PrgID">
<xsl:copy-of select="Details" />
</xsl:when>
<xsl:otherwise>
<xsl:element name="Details">
<xsl:attribute name="PrgID">OECTrans.ImportTrans</xsl:attribute>
<xsl:for-each select="Details/@*">
<xsl:copy-of select="." />
</xsl:for-each>
<xsl:for-each select="Details/*">
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
<xsl:element name="Header">
<xsl:attribute name="RONum">
<xsl:value-of select="//Envelope/@RONum" />
</xsl:attribute>
<xsl:attribute name="OwnerFName">
<xsl:value-of select="//Admin/@OwnerF"/>
</xsl:attribute>
<xsl:attribute name="OwnerLName">
<xsl:value-of select="//Admin/@OwnerL"/>
</xsl:attribute>
<xsl:attribute name="VIN">
<xsl:value-of select="//Vehicle/@TransVIN" />
</xsl:attribute>
<xsl:attribute name="Mileage">
<xsl:value-of select="//Vehicle/@TransMileage" />
</xsl:attribute>
<xsl:attribute name="Year">
<xsl:value-of select="//Vehicle/@TransYear" />
</xsl:attribute>
<xsl:attribute name="Make">
<xsl:if test="//Vehicle/@ManufName[. != '']" >
<xsl:choose>
<xsl:when test="//Vehicle/@ManufName[. = 'Geo']">CHEV</xsl:when>
<xsl:when test="//Vehicle/@ManufName[. = 'Chev-GMC Truck']">CHEV</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Vehicle/@ManufName" />
</xsl:otherwise>
</xsl:choose>
</xsl:if>
<xsl:if test="//Vehicle/@ManufName[. = '']" >
<xsl:value-of select="//Vehicle/@ManufCode" />
</xsl:if>
</xsl:attribute>
<xsl:attribute name="Model">
<xsl:value-of select="//Vehicle/@TransModel" />
</xsl:attribute>
<xsl:attribute name="Description">
<xsl:value-of select="//Vehicle/@TransYear" />
<xsl:text> </xsl:text>
<xsl:value-of select="//Vehicle/@ManufName" />
<xsl:text> </xsl:text>
<xsl:value-of select="//Vehicle/@TransModel" />
</xsl:attribute>
<xsl:attribute name="LastSupplLevel">
<xsl:choose>
<xsl:when test="//Envelope/@TransactionType = 'E'">0</xsl:when>
<xsl:when test="substring(//Envelope/@SupplementNum, 1, 1) = 'S'">
<xsl:value-of select="substring(//Envelope/@SupplementNum, 2)" />
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:element>
<xsl:apply-templates select="PartsList" />
<xsl:element name="Envelope">
<xsl:element name="Software">
<xsl:attribute name="Manifest">Audatex.xml</xsl:attribute>
<xsl:attribute name="Descriptor">Audatex.xsl</xsl:attribute>
<xsl:attribute name="System">
<xsl:value-of select="//Envelope/@EstimatingSystem"/>
</xsl:attribute>
<xsl:attribute name="Version">
<xsl:value-of select="//Envelope/@SoftwareVersion"/>
</xsl:attribute>
<xsl:attribute name="SystemName">
<xsl:value-of select="//Envelope/@SystemName"/>
</xsl:attribute>
<xsl:attribute name="EstimateFileID">
<xsl:value-of select="//Envelope/@EstimateFileID"/>
</xsl:attribute>
<xsl:attribute name="UniqueFileID">
<xsl:value-of select="//Envelope/@UniqueFileID"/>
</xsl:attribute>
<xsl:attribute name="EstimateID">
<xsl:value-of select="//Envelope/@EstimatingSystem"/>-<xsl:value-of select="//Envelope/@SoftwareVersion"/>-<xsl:value-of select="//Envelope/@UniqueFileID"/>
</xsl:attribute>
<xsl:attribute name="EstimateIDv2">
<xsl:value-of select="//Envelope/@EstimatingSystem"/>-<xsl:value-of select="//Envelope/@UniqueFileID"/>
</xsl:attribute>
<xsl:attribute name="EstimateFormatVersion">
<xsl:value-of select="//Envelope/@EMSVersion"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
<xsl:element name="Totals">
<xsl:attribute name="GrandTotalAmount">
<xsl:value-of select="//Total/@GrandTotalAmount"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="Administrative">
<xsl:element name="Owner">
<xsl:attribute name="FName">
<xsl:value-of select="//Admin/@OwnerF"/>
</xsl:attribute>
<xsl:attribute name="LName">
<xsl:value-of select="//Admin/@OwnerL"/>
</xsl:attribute>
<xsl:attribute name="Company">
<!--<xsl:value-of select="//Admin/@OwnerCompanyName"/>-->
</xsl:attribute>
<xsl:attribute name="Address">
<!--<xsl:value-of select="//Admin/@OwnerAddr1"/>-->
</xsl:attribute>
<xsl:attribute name="Address2">
<!--<xsl:value-of select="//Admin/@OwnerAddr2"/>-->
</xsl:attribute>
<xsl:attribute name="City">
<!--<xsl:value-of select="//Admin/@OwnerCity"/>-->
</xsl:attribute>
<xsl:attribute name="State">
<xsl:value-of select="//Admin/@OwnerState"/>
</xsl:attribute>
<xsl:attribute name="Zip">
<!--<xsl:choose>
<xsl:when test="contains(//Admin/@OwnerZip, '-')">
<xsl:choose>
<xsl:when test="string-length(substring-before(//Admin/@OwnerZip, '-'))=5">
<xsl:choose>
<xsl:when test="string-length(substring-after(//Admin/@OwnerZip, '-'))=4">
<xsl:value-of select="//Admin/@OwnerZip"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(//Admin/@OwnerZip, 1, 5)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:when>
<xsl:when test="string-length(//Admin/@OwnerZip)=9">
<xsl:value-of select="substring(//Admin/@OwnerZip, 1, 5)"/>-<xsl:value-of select="substring(//Admin/@OwnerZip, 6, 4)"/>
</xsl:when>
<xsl:when test="string-length(//Admin/@OwnerZip)&gt;4">
<xsl:value-of select="substring(//Admin/@OwnerZip, 1, 5)"/>
</xsl:when>
</xsl:choose>-->
</xsl:attribute>
<xsl:attribute name="Country">
<!--<xsl:value-of select="//Admin/@OwnerCountry"/>-->
</xsl:attribute>
<xsl:attribute name="Phone">
<!--<xsl:choose>
<xsl:when test="//Admin/@OwnerPhone1Ext[. != '']">
<xsl:value-of select="//Admin/@OwnerPhone1"/>&#32;x<xsl:value-of select="//Admin/@OwnerPhone1Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@OwnerPhone1"/>
</xsl:otherwise>
</xsl:choose>-->
</xsl:attribute>
<xsl:attribute name="Phone2">
<!--<xsl:choose>
<xsl:when test="//Admin/@OwnerPhone2Ext[. != '']">
<xsl:value-of select="//Admin/@OwnerPhone2"/>&#32;x<xsl:value-of select="//Admin/@OwnerPhone2Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@OwnerPhone2"/>
</xsl:otherwise>
</xsl:choose>-->
</xsl:attribute>
<xsl:attribute name="Fax">
<!--<xsl:choose>
<xsl:when test="//Admin/@OwnerFaxExt[. != '']">
<xsl:value-of select="//Admin/@OwnerFax"/>&#32;x<xsl:value-of select="//Admin/@OwnerFaxExt"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@OwnerFax"/>
</xsl:otherwise>
</xsl:choose>-->
</xsl:attribute>
<xsl:attribute name="Email">
<!--<xsl:value-of select="//Admin/@OwnerEmail"/>-->
</xsl:attribute>
</xsl:element>
<xsl:element name="InsuranceCompany">
<xsl:attribute name="Id">
<xsl:value-of select="//Admin/@InsuranceCompanyID"/>
</xsl:attribute>
<xsl:attribute name="Name">
<xsl:value-of select="//Admin/@InsuranceCompanyName"/>
</xsl:attribute>
<xsl:attribute name="Address">
<xsl:value-of select="//Admin/@InsuranceAddr1"/>
</xsl:attribute>
<xsl:attribute name="Address2">
<xsl:value-of select="//Admin/@InsuranceAddr2"/>
</xsl:attribute>
<xsl:attribute name="City">
<xsl:value-of select="//Admin/@InsuranceCity"/>
</xsl:attribute>
<xsl:attribute name="State">
<xsl:value-of select="//Admin/@InsuranceState"/>
</xsl:attribute>
<xsl:attribute name="Zip">
<xsl:choose>
<xsl:when test="contains(//Admin/@InsuranceZip, '-')">
<xsl:choose>
<xsl:when test="string-length(substring-before(//Admin/@InsuranceZip, '-'))=5">
<xsl:choose>
<xsl:when test="string-length(substring-after(//Admin/@InsuranceZip, '-'))=4">
<xsl:value-of select="//Admin/@InsuranceZip"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(//Admin/@InsuranceZip, 1, 5)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:when>
<xsl:when test="string-length(//Admin/@InsuranceZip)=9">
<xsl:value-of select="substring(//Admin/@InsuranceZip, 1, 5)"/>-<xsl:value-of select="substring(//Admin/@InsuranceZip, 6, 4)"/>
</xsl:when>
<xsl:when test="string-length(//Admin/@InsuranceZip)&gt;4">
<xsl:value-of select="substring(//Admin/@InsuranceZip, 1, 5)"/>
</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Country">
<xsl:value-of select="//Admin/@InsuranceCountry"/>
</xsl:attribute>
<xsl:attribute name="Phone">
<xsl:choose>
<xsl:when test="//Admin/@InsurancePhone1Ext[. != '']">
<xsl:value-of select="//Admin/@InsurancePhone1"/>&#32;x<xsl:value-of select="//Admin/@InsurancePhone1Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@InsurancePhone1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Phone2">
<xsl:choose>
<xsl:when test="//Admin/@InsurancePhone2Ext[. != '']">
<xsl:value-of select="//Admin/@InsurancePhone2"/>&#32;x<xsl:value-of select="//Admin/@InsurancePhone2Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@InsurancePhone2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Fax">
<xsl:choose>
<xsl:when test="//Admin/@InsuranceFaxExt[. != '']">
<xsl:value-of select="//Admin/@InsuranceFax"/>&#32;x<xsl:value-of select="//Admin/@InsuranceFaxExt"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@InsuranceFax"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Email">
<xsl:value-of select="//Admin/@InsuranceEmail"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="Insured">
<xsl:attribute name="LastName">
<xsl:value-of select="//Admin/@InsuranceLName" />
</xsl:attribute>
</xsl:element>
<xsl:element name="Claim">
<xsl:attribute name="Number">
<xsl:value-of select="//Admin/@ClaimNumber"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="Estimator">
<xsl:attribute name="FName">
<xsl:value-of select="//Admin2/@EstimatorF"/>
</xsl:attribute>
<xsl:attribute name="LName">
<xsl:value-of select="//Admin2/@EstimatorL"/>
</xsl:attribute>
<xsl:attribute name="Company">
<xsl:value-of select="//Admin2/@EstimatorCompanyName"/>
</xsl:attribute>
<xsl:attribute name="BodyShopCity">
<xsl:value-of select="//Admin2/@BodyShopCity"/>
</xsl:attribute>
<xsl:attribute name="BodyShopName">
<xsl:value-of select="//Admin2/@BodyShopName"/>
</xsl:attribute>
<xsl:attribute name="Address">
<xsl:value-of select="//Admin2/@EstimatorAddr1"/>
</xsl:attribute>
<xsl:attribute name="Address2">
<xsl:value-of select="//Admin2/@EstimatorAddr2"/>
</xsl:attribute>
<xsl:attribute name="City">
<xsl:value-of select="//Admin2/@EstimatorCity"/>
</xsl:attribute>
<xsl:attribute name="State">
<xsl:value-of select="//Admin2/@EstimatorState"/>
</xsl:attribute>
<xsl:attribute name="Zip">
<xsl:choose>
<xsl:when test="contains(//Admin2/@EstimatorZip, '-')">
<xsl:choose>
<xsl:when test="string-length(substring-before(//Admin2/@EstimatorZip, '-'))=5">
<xsl:choose>
<xsl:when test="string-length(substring-after(//Admin2/@EstimatorZip, '-'))=4">
<xsl:value-of select="//Admin2/@EstimatorZip"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(//Admin2/@EstimatorZip, 1, 5)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:when>
<xsl:when test="string-length(//Admin2/@EstimatorZip)=9">
<xsl:value-of select="substring(//Admin2/@EstimatorZip, 1, 5)"/>-<xsl:value-of select="substring(//Admin2/@EstimatorZip, 6, 4)"/>
</xsl:when>
<xsl:when test="string-length(//Admin2/@EstimatorZip)&gt;4">
<xsl:value-of select="substring(//Admin2/@EstimatorZip, 1, 5)"/>
</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Country">
<xsl:value-of select="//Admin2/@EstimatorCountry"/>
</xsl:attribute>
<xsl:attribute name="Phone">
<xsl:choose>
<xsl:when test="//Admin2/@EstimatorPhone1Ext[. != '']">
<xsl:value-of select="//Admin2/@EstimatorPhone1"/>&#32;x<xsl:value-of select="//Admin2/@EstimatorPhone1Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin2/@EstimatorPhone1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Phone2">
<xsl:choose>
<xsl:when test="//Admin2/@EstimatorPhone2Ext[. != '']">
<xsl:value-of select="//Admin2/@EstimatorPhone2"/>&#32;x<xsl:value-of select="//Admin2/@EstimatorPhone2Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin2/@EstimatorPhone2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Fax">
<xsl:choose>
<xsl:when test="//Admin2/@EstimatorFaxExt[. != '']">
<xsl:value-of select="//Admin2/@EstimatorFax"/>&#32;x<xsl:value-of select="//Admin2/@EstimatorFaxExt"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin2/@EstimatorFax"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Email">
<xsl:value-of select="//Admin2/@EstimatorEmail"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="RepairInformation">
<xsl:attribute name="VehicleDateIn">
<xsl:if test="string-length(//Admin2/@VehicleInDate)&gt;6">
<xsl:value-of select="substring-before(//Admin2/@VehicleInDate, ' ')"/>
<xsl:if test="string-length(//Admin2/@VehicleInTime)=4">
&#32;<xsl:value-of select="substring(//Admin2/@VehicleInTime, 1, 2)"/>:<xsl:value-of select="substring(//Admin2/@VehicleInTime, 3, 2)"/>
</xsl:if>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="EstimatedVehicleDateOut">
<xsl:if test="string-length(//Admin2/@TargetVehicleOutDate)&gt;6">
<xsl:value-of select="substring-before(//Admin2/@TargetVehicleOutDate,' ')"/>
<xsl:if test="string-length(//Admin2/@TargetVehicleOutTime)=4">
&#32;<xsl:value-of select="substring(//Admin2/@TargetVehicleOutTime, 1, 2)"/>:<xsl:value-of select="substring(//Admin2/@TargetVehicleOutTime, 3, 2)"/>
</xsl:if>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="VehicleDateOut">
<xsl:if test="string-length(//Admin2/@VehicleOutDate)&gt;6">
<xsl:value-of select="substring-before(//Admin2/@VehicleOutDate, ' ')"/>
<xsl:if test="string-length(//Admin2/@VehicleOutTime)=4">
&#32;<xsl:value-of select="substring(//Admin2/@VehicleOutTime, 1, 2)"/>:<xsl:value-of select="substring(//Admin2/@VehicleOutTime, 3, 2)"/>
</xsl:if>
</xsl:if>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template match="PartsList">
<xsl:element name="PartsList" >
<xsl:for-each select="Part">
<xsl:element name="Part" >
<!-- Part number translation rules -->
<xsl:variable name="OEMPartNumber">
<xsl:if test="substring(@TDPartNum, string-length(@TDPartNum) - 7) != ' GM PART'">
<xsl:value-of select="@TDPartNum" />
</xsl:if>
<xsl:if test="substring(@TDPartNum, string-length(@TDPartNum) - 7) = ' GM PART'">
<xsl:value-of select="substring-before(@TDPartNum,' GM PART')" />
</xsl:if>
</xsl:variable>
<xsl:variable name="AltPartNumber">
<xsl:value-of select="@AltPartNum"/>
</xsl:variable>
<xsl:variable name="PrimaryPartType">
<xsl:value-of select="@PartType"/>
</xsl:variable>
<xsl:variable name="PrimaryPartNumber">
<xsl:choose>
<xsl:when test="@PartType='PAN' or @PartType='PAG'">
<xsl:value-of select="$OEMPartNumber"/>
</xsl:when>
<xsl:when test="@PartType!='PAN' and @PartType!='PAG'">
<xsl:choose>
<xsl:when test="$AltPartNumber!=''">
<xsl:value-of select="$AltPartNumber"/>
</xsl:when>
<xsl:when test="$OEMPartNumber!=''">
<xsl:value-of select="$OEMPartNumber"/>
</xsl:when>
<xsl:otherwise>Salvage or Assembly</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="SecondaryPartNumber">
<xsl:choose>
<xsl:when test="@PartType!='PAN'">
<xsl:value-of select="$OEMPartNumber"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="SecondaryPartType">
<xsl:choose>
<xsl:when test="@PartType='PAA'">PAN</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:attribute name="TDPartNum">
<xsl:value-of select="$PrimaryPartNumber"/>
</xsl:attribute>
<xsl:attribute name="AltPartNum">
<xsl:value-of select="$SecondaryPartNumber" />
</xsl:attribute>
<xsl:attribute name="ExternalPartType">
<xsl:value-of select="$PrimaryPartType" />
</xsl:attribute>
<xsl:attribute name="ExternalAltPartType">
<xsl:value-of select="$SecondaryPartType" />
</xsl:attribute>
<xsl:attribute name="TDPartType">
<xsl:choose>
<xsl:when test="$PrimaryPartType='PAN' or $PrimaryPartType='PAP'">1</xsl:when>
<xsl:when test="$PrimaryPartType='PAA' or $PrimaryPartType='PATR'">2</xsl:when>
<xsl:when test="$PrimaryPartType='PAL'">3</xsl:when>
<xsl:when test="$PrimaryPartType='PAM' or $PrimaryPartType='PAC' or $PrimaryPartType='PAR'">4</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="AltPartType">
<xsl:choose>
<xsl:when test="$SecondaryPartType='PAN'">1</xsl:when>
<xsl:when test="$SecondaryPartType='PAA' or $SecondaryPartType='PATR'">2</xsl:when>
<xsl:when test="$SecondaryPartType='PAL'">3</xsl:when>
<xsl:when test="$SecondaryPartType='PAM' or $SecondaryPartType='PAC' or $SecondaryPartType='PAR'">4</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="TDPartDesc">
<xsl:value-of select="@TDPartDesc" />
</xsl:attribute>
<xsl:attribute name="TDEstimate">
<xsl:choose>
<xsl:when test="@PartType='PAN'">
<xsl:value-of select="@TDEstimate" />
</xsl:when>
<xsl:when test="@PartType='PAM'">
<xsl:value-of select="@ActPrice" />
</xsl:when>
<xsl:when test="@PartType='PAA'">
<xsl:value-of select="@ActPrice" />
</xsl:when>
<xsl:when test="@PartType='PAL'">
<xsl:value-of select="@ActPrice" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@ActPrice" />
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="TDPartQty">
<xsl:value-of select="@TDPartQty" />
</xsl:attribute>
<xsl:attribute name="LineNumber">
<xsl:value-of select="@LineNumber" />
</xsl:attribute>
<xsl:attribute name="SequenceNumber">
<xsl:value-of select="@SequenceNumber" />
</xsl:attribute>
<xsl:attribute name="SupplLevel">
<xsl:choose>
<xsl:when test="@SupplementLevel = 'E'">0</xsl:when>
<xsl:when test="substring(@SupplementLevel, 1, 1) = 'S'">
<xsl:value-of select="substring(@SupplementLevel, 2)" />
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="LaborType">
<xsl:value-of select="@LaborType" />
</xsl:attribute>
<xsl:attribute name="LaborHours">
<xsl:value-of select="@LaborHours" />
</xsl:attribute>
<xsl:attribute name="OperationCode">
<xsl:value-of select="@LaborOp" />
</xsl:attribute>
<xsl:attribute name="PriceIncluded">
<xsl:value-of select="@PriceIncluded" />
</xsl:attribute>
<xsl:attribute name="MarkUp">
<xsl:value-of select="@MarkUp" />
</xsl:attribute>
<xsl:attribute name="CLPart">
<!-- CLPart is false if TRAN_CODE == 2 or TRAN_CODE == 3 -->
<!-- CLPart is false if PartType is not 'PAN','PAP','PAL','PAG', 'PAM', or 'PAA', or 'PAC' or 'PATR' -->
<!-- CLPart is false if LaborCode is only labor -->
<!-- CLPart is false if OpCode is not "Remove/Replace" or "Remove/Replace Partial" -->
<!-- If LaborOp is OP1, then allow the part to import CP 12/28/08 for Shop Client Release 4.1.4 -->
<xsl:choose>
<xsl:when test="@TransactionCode='3'">False</xsl:when>
<xsl:when test="@TransactionCode='1' or @TransactionCode=' ' or @TransactionCode='' or @TransactionCode='2'">
<xsl:choose>
<xsl:when test="@PartType='PAN' or @PartType='PAG' or @PartType='PAM' or @PartType='PAP' or @PartType='PAL' or @PartType='PAA' or @PartType='PAC' or @PartType='PATR'">
<xsl:choose>
<xsl:when test="@LaborType='LAD' or @LaborType='LAE' or @LaborType='LAU' or @LaborType='LAT'">False</xsl:when>
<xsl:otherwise>
<xsl:choose>
<!--xsl:when test="@LaborOp='OP0'">False</xsl:when -->
<xsl:when test="@LaborOp='OP1'">False</xsl:when>
<xsl:when test="@LaborOp='OP2'">False</xsl:when>
<xsl:when test="@LaborOp='OP3'">False</xsl:when>
<xsl:when test="@LaborOp='OP4'">False</xsl:when>
<xsl:when test="@LaborOp='OP5'">False</xsl:when>
<xsl:when test="@LaborOp='OP6'">False</xsl:when>
<xsl:when test="@LaborOp='OP7'">False</xsl:when>
<xsl:when test="@LaborOp='OP8'">False</xsl:when>
<xsl:when test="@LaborOp='OP9'">False</xsl:when>
<xsl:when test="@LaborOp='OP10'">False</xsl:when>
<xsl:when test="@LaborOp='OP13'">False</xsl:when>
<xsl:when test="@LaborOp='OP14'">False</xsl:when>
<xsl:when test="@LaborOp='OP15'">False</xsl:when>
<xsl:when test="@LaborOp='OP16'">False</xsl:when>
<xsl:otherwise>True</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>False</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>False</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,131 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TranslationDescriptor>
<FileTranslations FormatName="ADP EMS" ElementName="Estimate" Description="Translation Descriptor for a ADP ShopLink Estimate, Version EMS 2.0">
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.env">
<DBFFileTranslation ID="ID1" ElementName="Envelope" Description="Envelope Table">
<DBFFieldSpec FieldName="RO_ID" Form="attribute" Name="RONum" FieldType="" Description="RO Number"/>
<DBFFieldSpec FieldName="UNQFILE_ID" Form="attribute" Name="UniqueFileID" FieldType="" Description="Unique File Identifier"/>
<DBFFieldSpec FieldName="ESTFILE_ID" Form="attribute" Name="EstimateFileID" FieldType="" Description="Estimate File Identifier"/>
<DBFFieldSpec FieldName="INCL_ADMIN" Form="attribute" Name="IncludesAdminInfo" FieldType="Boolean" Description="Includes Admin Info Flag"/>
<DBFFieldSpec FieldName="INCL_VEH" Form="attribute" Name="IncludesVehicleInfo" FieldType="Boolean" Description="Includes Vehicle Info Flag"/>
<DBFFieldSpec FieldName="INCL_EST" Form="attribute" Name="IncludesEstimateInfo" FieldType="Boolean" Description="Includes Estimate Info Flag"/>
<DBFFieldSpec FieldName="INCL_PROFL" Form="attribute" Name="IncludesProfileInfo" FieldType="Boolean" Description="Includes Profile Info Flag"/>
<DBFFieldSpec FieldName="INCL_TOTAL" Form="attribute" Name="IncludesTotalsInfo" FieldType="Boolean" Description="Includes Totals Info Flag"/>
<DBFFieldSpec FieldName="INCL_VENDR" Form="attribute" Name="IncludesVendorInfo" FieldType="Boolean" Description="Includes Vendor Info Flag"/>
<DBFFieldSpec FieldName="EMS_VER" Form="attribute" Name="EMSVersion" FieldType="" Description="EMS Version Number"/>
<DBFFieldSpec FieldName="SUPP_NO" Form="attribute" Name="SupplementNum" FieldType="" Description="Supplement Number"/>
<DBFFieldSpec FieldName="TRANS_TYPE" Form="attribute" Name="TransactionType" FieldType="" Description="Transaction Type"/>
<DBFFieldSpec FieldName="EST_SYSTEM" Form="attribute" Name="EstimatingSystem" FieldType="" Description="Estimating System Software"/>
<DBFFieldSpec FieldName="SW_VERSION" Form="attribute" Name="SoftwareVersion" FieldType="" Description="Software Version Identifier"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.LIN">
<DBFFileTranslation ID="ID2" ElementSetName="PartsList" ElementName="Part" Description="Parts List Table">
<DBFFieldSpec FieldName="OEM_PARTNO" Form="attribute" Name="TDPartNum" FieldType="" Description="Part Number"/>
<DBFFieldSpec FieldName="ALT_PARTNO" Form="attribute" Name="AltPartNum" FieldType="" Description="Alternate Part Number"/>
<DBFFieldSpec FieldName="ACT_PRICE" Form="attribute" Name="ActPrice" FieldType="" Description="Actual Part Price"/>
<DBFFieldSpec FieldName="LINE_DESC" Form="attribute" Name="TDPartDesc" FieldType="" Description="Part Description"/>
<DBFFieldSpec FieldName="DB_PRICE" Form="attribute" Name="TDEstimate" FieldType="" Description="DB_Price"/>
<DBFFieldSpec FieldName="PART_QTY" Form="attribute" Name="TDPartQty" FieldType="" Description="Part Quantity"/>
<DBFFieldSpec FieldName="PART_TYPE" Form="attribute" Name="PartType" FieldType="" Description="Part Type"/>
<DBFFieldSpec FieldName="MOD_LBR_TY" Form="attribute" Name="LaborType" FieldType="" Description="Labor Type"/>
<DBFFieldSpec FieldName="MOD_LB_HRS" Form="attribute" Name="LaborHours" FieldType="" Description="Labor Hours"/>
<DBFFieldSpec FieldName="LBR_OP" Form="attribute" Name="LaborOp" FieldType="" Description="Labor Op"/>
<DBFFieldSpec FieldName="LINE_NO" Form="attribute" Name="LineNumber" FieldType="" Description="Line number"/>
<DBFFieldSpec FieldName="UNQ_SEQ" Form="attribute" Name="SequenceNumber" FieldType="" Description=""/>
<DBFFieldSpec FieldName="LINE_IND" Form="attribute" Name="SupplementLevel" FieldType="" Description="Supplement Level"/>
<DBFFieldSpec FieldName="TRAN_CODE" Form="attribute" Name="TransactionCode" FieldType="" Description="Transaction Code"/>
<DBFFieldSpec FieldName="PRICE_INC" Form="attribute" Name="PriceIncluded" FieldType="" Description="Price Included"/>
<DBFFieldSpec FieldName="PRT_DSMK_P" Form="attribute" Name="MarkUp" FieldType="" Description="Price Mark up for non OEM parts"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.VEH">
<DBFFileTranslation ID="ID3" ElementName="Vehicle" Description="Vehicle Table">
<DBFFieldSpec FieldName="V_VIN" Form="attribute" Name="TransVIN" FieldType="" Description=""/>
<DBFFieldSpec FieldName="V_MODEL_YR" Form="attribute" Name="TransYear" FieldType="" Description="V_MODEL_YR"/>
<DBFFieldSpec FieldName="V_MAKEDESC" Form="attribute" Name="ManufName" FieldType="" Description="V_MAKEDESC"/>
<DBFFieldSpec FieldName="V_MAKECODE" Form="attribute" Name="ManufCode" FieldType="" Description="V_MAKECODE"/>
<DBFFieldSpec FieldName="V_MODEL" Form="attribute" Name="TransModel" FieldType="" Description="V_MODEL"/>
<DBFFieldSpec FieldName="V_MILEAGE" Form="attribute" Name="TransMileage" FieldType="" Description="V_MILEAGE"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.AD1">
<DBFFileTranslation ID="ID4" ElementName="Admin" Description="Administrative information">
<DBFFieldSpec FieldName="OWNR_LN" Form="attribute" Name="OwnerL" FieldType="" Description="Owner Lastname"/>
<DBFFieldSpec FieldName="OWNR_FN" Form="attribute" Name="OwnerF" FieldType="" Description="Owner Firstname"/>
<DBFFieldSpec FieldName="OWNR_CO_NM" Form="attribute" Name="OwnerCompanyName" FieldType="" Description="Owner Company Name"/>
<DBFFieldSpec FieldName="OWNR_TITLE" Form="attribute" Name="OwnerTitle" FieldType="" Description="Owner Title"/>
<DBFFieldSpec FieldName="OWNR_ADDR1" Form="attribute" Name="OwnerAddr1" FieldType="" Description="Owner Address Line 1"/>
<DBFFieldSpec FieldName="OWNR_ADDR2" Form="attribute" Name="OwnerAddr2" FieldType="" Description="Owner Address Line 2"/>
<DBFFieldSpec FieldName="OWNR_CITY" Form="attribute" Name="OwnerCity" FieldType="" Description="Owner City"/>
<DBFFieldSpec FieldName="OWNR_ST" Form="attribute" Name="OwnerState" FieldType="" Description="Owner State"/>
<DBFFieldSpec FieldName="OWNR_ZIP" Form="attribute" Name="OwnerZip" FieldType="" Description="Owner Zip"/>
<DBFFieldSpec FieldName="OWNR_CTRY" Form="attribute" Name="OwnerCountry" FieldType="" Description="Owner Country"/>
<DBFFieldSpec FieldName="OWNR_PH1" Form="attribute" Name="OwnerPhone1" FieldType="" Description="Owner Primary Phone"/>
<DBFFieldSpec FieldName="OWNR_PH1X" Form="attribute" Name="OwnerPhone1Ext" FieldType="" Description="Owner Primary Phone Extension"/>
<DBFFieldSpec FieldName="OWNR_PH2" Form="attribute" Name="OwnerPhone2" FieldType="" Description="Owner Secondary Phone"/>
<DBFFieldSpec FieldName="OWNR_PH2X" Form="attribute" Name="OwnerPhone2Ext" FieldType="" Description="Owner Secondary Phone Extension"/>
<DBFFieldSpec FieldName="OWNR_FAX" Form="attribute" Name="OwnerFax" FieldType="" Description="Owner Fax"/>
<DBFFieldSpec FieldName="OWNR_FAXX" Form="attribute" Name="OwnerFaxExt" FieldType="" Description="Owner Fax Extension"/>
<DBFFieldSpec FieldName="OWNR_EA" Form="attribute" Name="OwnerEmail" FieldType="" Description="Owner Email Address"/>
<DBFFieldSpec FieldName="INS_CO_NM" Form="attribute" Name="InsuranceCompanyName" FieldType="" Description="Insurance Company Name"/>
<DBFFieldSpec FieldName="INS_CO_ID" Form="attribute" Name="InsuranceCompanyID" FieldType="" Description="Insurance Company Identifier"/>
<DBFFieldSpec FieldName="INS_ADDR1" Form="attribute" Name="InsuranceAddr1" FieldType="" Description="Insurance Address Line 1"/>
<DBFFieldSpec FieldName="INS_ADDR2" Form="attribute" Name="InsuranceAddr2" FieldType="" Description="Insurance Address Line 2"/>
<DBFFieldSpec FieldName="INS_CITY" Form="attribute" Name="InsuranceCity" FieldType="" Description="Insurance City"/>
<DBFFieldSpec FieldName="INS_ST" Form="attribute" Name="InsuranceState" FieldType="" Description="Insurance State"/>
<DBFFieldSpec FieldName="INS_ZIP" Form="attribute" Name="InsuranceZip" FieldType="" Description="Insurance Zip"/>
<DBFFieldSpec FieldName="INS_CTRY" Form="attribute" Name="InsuranceCountry" FieldType="" Description="Insurance Country"/>
<DBFFieldSpec FieldName="INS_PH1" Form="attribute" Name="InsurancePhone1" FieldType="" Description="Insurance Primary Phone"/>
<DBFFieldSpec FieldName="INS_PH1X" Form="attribute" Name="InsurancePhone1Ext" FieldType="" Description="Insurance Primary Phone Extension"/>
<DBFFieldSpec FieldName="INS_PH2" Form="attribute" Name="InsurancePhone2" FieldType="" Description="Insurance Secondary Phone"/>
<DBFFieldSpec FieldName="INS_PH2X" Form="attribute" Name="InsurancePhone2Ext" FieldType="" Description="Insurance Secondary Phone Extension"/>
<DBFFieldSpec FieldName="INS_FAX" Form="attribute" Name="InsuranceFax" FieldType="" Description="Insurance Fax"/>
<DBFFieldSpec FieldName="INS_FAXX" Form="attribute" Name="InsuranceFaxExt" FieldType="" Description="Insurance Fax Extension"/>
<DBFFieldSpec FieldName="INS_EA" Form="attribute" Name="InsuranceEmail" FieldType="" Description="Insurance Email Address"/>
<DBFFieldSpec FieldName="INSD_LN" Form="attribute" Name="InsuranceLName" FieldType="" Description="Insurance Last Name"/>
<DBFFieldSpec FieldName="CLM_NO" Form="attribute" Name="ClaimNumber" FieldType="" Description="Claim Number"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.AD2">
<DBFFileTranslation ID="ID5" ElementName="Admin2" Description="Administrative information2">
<DBFFieldSpec FieldName="EST_CT_LN" Form="attribute" Name="EstimatorL" FieldType="" Description="Estimator Lastname"/>
<DBFFieldSpec FieldName="EST_CT_FN" Form="attribute" Name="EstimatorF" FieldType="" Description="Estimator Firstname"/>
<DBFFieldSpec FieldName="EST_CO_NM" Form="attribute" Name="EstimatorCompanyName" FieldType="" Description="Estimator Company Name"/>
<DBFFieldSpec FieldName="EST_CO_ID" Form="attribute" Name="EstimatorCompanyID" FieldType="" Description="Estimator Company Identifier"/>
<DBFFieldSpec FieldName="EST_ADDR1" Form="attribute" Name="EstimatorAddr1" FieldType="" Description="Estimator Address1"/>
<DBFFieldSpec FieldName="EST_ADDR2" Form="attribute" Name="EstimatorAddr2" FieldType="" Description="Estimator Address2"/>
<DBFFieldSpec FieldName="EST_CITY" Form="attribute" Name="EstimatorCity" FieldType="" Description="Estimator City"/>
<DBFFieldSpec FieldName="EST_ST" Form="attribute" Name="EstimatorState" FieldType="" Description="Estimator State"/>
<DBFFieldSpec FieldName="EST_ZIP" Form="attribute" Name="EstimatorZip" FieldType="" Description="Estimator Zip"/>
<DBFFieldSpec FieldName="EST_CTRY" Form="attribute" Name="EstimatorCountry" FieldType="" Description="Estimator Country"/>
<DBFFieldSpec FieldName="EST_PH1" Form="attribute" Name="EstimatorPhone1" FieldType="" Description="Estimator Primary Phone"/>
<DBFFieldSpec FieldName="EST_PH1X" Form="attribute" Name="EstimatorPhone1Ext" FieldType="" Description="Estimator Primary Phone Extension"/>
<DBFFieldSpec FieldName="EST_PH2" Form="attribute" Name="EstimatorPhone2" FieldType="" Description="Estimator Secondary Phone"/>
<DBFFieldSpec FieldName="EST_PH2X" Form="attribute" Name="EstimatorPhone2Ext" FieldType="" Description="Estimator Secondary Phone Extension"/>
<DBFFieldSpec FieldName="EST_FAX" Form="attribute" Name="EstimatorFax" FieldType="" Description="Estimator Fax"/>
<DBFFieldSpec FieldName="EST_FAXX" Form="attribute" Name="EstimatorFaxExt" FieldType="" Description="Estimator Fax Extension"/>
<DBFFieldSpec FieldName="EST_EA" Form="attribute" Name="EstimatorEmail" FieldType="" Description="Estimator Email Address"/>
<DBFFieldSpec FieldName="EST_LIC_NO" Form="attribute" Name="EstimatorLicenseNumber" FieldType="" Description="Estimator License Number"/>
<DBFFieldSpec FieldName="EST_FILENO" Form="attribute" Name="EstimatorFileNumber" FieldType="" Description="Estimator File Number"/>
<DBFFieldSpec FieldName="RO_IN_DATE" Form="attribute" Name="VehicleInDate" FieldType="" Description="Date arrived in shop"/>
<DBFFieldSpec FieldName="RO_IN_TIME" Form="attribute" Name="VehicleInTime" FieldType="" Description="Time arrived in shop"/>
<DBFFieldSpec FieldName="TAR_DATE" Form="attribute" Name="TargetVehicleOutDate" FieldType="" Description="Target date to be completed"/>
<DBFFieldSpec FieldName="TAR_TIME" Form="attribute" Name="TargetVehicleOutTime" FieldType="" Description="Target time to be completed"/>
<DBFFieldSpec FieldName="RO_CMPDATE" Form="attribute" Name="VehicleOutDate" FieldType="" Description="Date completed"/>
<DBFFieldSpec FieldName="RO_CMPTIME" Form="attribute" Name="VehicleOutTime" FieldType="" Description="Time completed"/>
<DBFFieldSpec FieldName="RF_CITY" Form="attribute" Name="BodyShopCity" FieldType="" Description="Body Shop City"/>
<DBFFieldSpec FieldName="RF_CO_NM" Form="attribute" Name="BodyShopName" FieldType="" Description="Body Shop Name"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.TTL">
<DBFFileTranslation ID="ID6" ElementName="Total" Description="Total Table">
<DBFFieldSpec FieldName="G_TTL_AMT" Form="attribute" Name="GrandTotalAmount" FieldType="" Description=""/>
</DBFFileTranslation>
</FileTranslation>
</FileTranslations>
<StyleSheetTranslation>
<StyleSheetURL>Audatex.xsl</StyleSheetURL>
</StyleSheetTranslation>
</TranslationDescriptor>

View File

@@ -1,607 +0,0 @@
<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<xsl:for-each select="//TranslatedData">
<xsl:element name="TranslatedData">
<xsl:attribute name="TranslationOutputFile">
<xsl:value-of select="@TranslationOutputFile"/>
</xsl:attribute>
<xsl:choose>
<xsl:when test="Details/@PrgID">
<xsl:copy-of select="Details" />
</xsl:when>
<xsl:otherwise>
<xsl:element name="Details">
<xsl:attribute name="PrgID">OECTrans.ImportTrans</xsl:attribute>
<xsl:for-each select="Details/@*">
<xsl:copy-of select="." />
</xsl:for-each>
<xsl:for-each select="Details/*">
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
<xsl:element name="Header">
<xsl:attribute name="RONum">
<xsl:value-of select="//Envelope/@RONum" />
</xsl:attribute>
<xsl:attribute name="OwnerFName">
<xsl:value-of select="//Admin/@OwnerF"/>
</xsl:attribute>
<xsl:attribute name="OwnerLName">
<xsl:value-of select="//Admin/@OwnerL"/>
</xsl:attribute>
<xsl:attribute name="VIN">
<xsl:value-of select="//Vehicle/@TransVIN" />
</xsl:attribute>
<xsl:attribute name="Mileage">
<xsl:value-of select="//Vehicle/@TransMileage" />
</xsl:attribute>
<xsl:attribute name="Year">
<xsl:value-of select="//Vehicle/@TransYear" />
</xsl:attribute>
<xsl:attribute name="Make">
<xsl:if test="//Vehicle/@ManufName[. != '']" >
<xsl:value-of select="//Vehicle/@ManufName" />
</xsl:if>
<xsl:if test="//Vehicle/@ManufName[. = '']" >
<xsl:value-of select="//Vehicle/@ManufCode" />
</xsl:if>
</xsl:attribute>
<xsl:attribute name="Model">
<xsl:value-of select="//Vehicle/@TransModel" />
</xsl:attribute>
<xsl:attribute name="Description">
<xsl:value-of select="//Vehicle/@TransYear" />
<xsl:text> </xsl:text>
<xsl:value-of select="//Vehicle/@ManufName" />
<xsl:text> </xsl:text>
<xsl:value-of select="//Vehicle/@TransModel" />
</xsl:attribute>
<xsl:attribute name="LastSupplLevel">
<xsl:choose>
<xsl:when test="//Envelope/@TransactionType = 'E'">0</xsl:when>
<xsl:when test="//Envelope/@TransactionType = 'S'">
<xsl:value-of select="substring(//Envelope/@SupplementNum, 2)" />
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:element>
<xsl:apply-templates select="PartsList" />
<xsl:element name="Envelope">
<xsl:element name="Software">
<xsl:attribute name="Manifest">CCC.xml</xsl:attribute>
<xsl:attribute name="Descriptor">CCC.xsl</xsl:attribute>
<xsl:attribute name="System">
<xsl:value-of select="//Envelope/@EstimatingSystem"/>
</xsl:attribute>
<xsl:attribute name="Version">
<xsl:value-of select="//Envelope/@SoftwareVersion"/>
</xsl:attribute>
<xsl:attribute name="SystemName">
<xsl:value-of select="//Envelope/@SystemName"/>
</xsl:attribute>
<xsl:attribute name="EstimateFileID">
<xsl:value-of select="//Envelope/@EstimateFileID"/>
</xsl:attribute>
<xsl:attribute name="UniqueFileID">
<xsl:value-of select="//Envelope/@UniqueFileID"/>
</xsl:attribute>
<xsl:attribute name="EstimateID">
<xsl:value-of select="//Envelope/@EstimatingSystem"/>-<xsl:value-of select="//Envelope/@SoftwareVersion"/>-<xsl:value-of select="//Envelope/@UniqueFileID"/>
</xsl:attribute>
<xsl:attribute name="EstimateIDv2">
<xsl:value-of select="//Envelope/@EstimatingSystem"/>-<xsl:value-of select="//Envelope/@UniqueFileID"/>
</xsl:attribute>
<xsl:attribute name="EstimateFormatVersion">
<xsl:value-of select="//Envelope/@EMSVersion"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
<xsl:element name="Totals">
<xsl:attribute name="GrandTotalAmount">
<xsl:value-of select="//Total/@GrandTotalAmount"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="Administrative">
<xsl:element name="Owner">
<xsl:attribute name="FName">
<xsl:value-of select="//Admin/@OwnerF"/>
</xsl:attribute>
<xsl:attribute name="LName">
<xsl:value-of select="//Admin/@OwnerL"/>
</xsl:attribute>
<xsl:attribute name="Company">
<!--<xsl:value-of select="//Admin/@OwnerCompanyName"/>-->
</xsl:attribute>
<xsl:attribute name="Address">
<!--<xsl:value-of select="//Admin/@OwnerAddr1"/>-->
</xsl:attribute>
<xsl:attribute name="Address2">
<!--<xsl:value-of select="//Admin/@OwnerAddr2"/>-->
</xsl:attribute>
<xsl:attribute name="City">
<!--<xsl:value-of select="//Admin/@OwnerCity"/>-->
</xsl:attribute>
<xsl:attribute name="State">
<xsl:value-of select="//Admin/@OwnerState"/>
</xsl:attribute>
<xsl:attribute name="Zip">
<!--<xsl:choose>
<xsl:when test="contains(//Admin/@OwnerZip, '-')">
<xsl:choose>
<xsl:when test="string-length(substring-before(//Admin/@OwnerZip, '-'))=5">
<xsl:choose>
<xsl:when test="string-length(substring-after(//Admin/@OwnerZip, '-'))=4">
<xsl:value-of select="//Admin/@OwnerZip"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(//Admin/@OwnerZip, 1, 5)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:when>
<xsl:when test="string-length(//Admin/@OwnerZip)=9">
<xsl:value-of select="substring(//Admin/@OwnerZip, 1, 5)"/>-<xsl:value-of select="substring(//Admin/@OwnerZip, 6, 4)"/>
</xsl:when>
<xsl:when test="string-length(//Admin/@OwnerZip)&gt;4">
<xsl:value-of select="substring(//Admin/@OwnerZip, 1, 5)"/>
</xsl:when>
</xsl:choose>-->
</xsl:attribute>
<xsl:attribute name="Country">
<!--<xsl:value-of select="//Admin/@OwnerCountry"/>-->
</xsl:attribute>
<xsl:attribute name="Phone">
<!--<xsl:choose>
<xsl:when test="//Admin/@OwnerPhone1Ext[. != '']">
<xsl:value-of select="//Admin/@OwnerPhone1"/>&#32;x<xsl:value-of select="//Admin/@OwnerPhone1Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@OwnerPhone1"/>
</xsl:otherwise>
</xsl:choose>-->
</xsl:attribute>
<xsl:attribute name="Phone2">
<!--<xsl:choose>
<xsl:when test="//Admin/@OwnerPhone2Ext[. != '']">
<xsl:value-of select="//Admin/@OwnerPhone2"/>&#32;x<xsl:value-of select="//Admin/@OwnerPhone2Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@OwnerPhone2"/>
</xsl:otherwise>
</xsl:choose>-->
</xsl:attribute>
<xsl:attribute name="Fax">
<!--<xsl:choose>
<xsl:when test="//Admin/@OwnerFaxExt[. != '']">
<xsl:value-of select="//Admin/@OwnerFax"/>&#32;x<xsl:value-of select="//Admin/@OwnerFaxExt"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@OwnerFax"/>
</xsl:otherwise>
</xsl:choose>-->
</xsl:attribute>
<xsl:attribute name="Email">
<!--<xsl:value-of select="//Admin/@OwnerEmail"/>-->
</xsl:attribute>
</xsl:element>
<xsl:element name="InsuranceCompany">
<xsl:attribute name="Id">
<xsl:value-of select="//Admin/@InsuranceCompanyID"/>
</xsl:attribute>
<xsl:attribute name="Name">
<xsl:value-of select="//Admin/@InsuranceCompanyName"/>
</xsl:attribute>
<xsl:attribute name="Address">
<xsl:value-of select="//Admin/@InsuranceAddr1"/>
</xsl:attribute>
<xsl:attribute name="Address2">
<xsl:value-of select="//Admin/@InsuranceAddr2"/>
</xsl:attribute>
<xsl:attribute name="City">
<xsl:value-of select="//Admin/@InsuranceCity"/>
</xsl:attribute>
<xsl:attribute name="State">
<xsl:value-of select="//Admin/@InsuranceState"/>
</xsl:attribute>
<xsl:attribute name="Zip">
<xsl:choose>
<xsl:when test="contains(//Admin/@InsuranceZip, '-')">
<xsl:choose>
<xsl:when test="string-length(substring-before(//Admin/@InsuranceZip, '-'))=5">
<xsl:choose>
<xsl:when test="string-length(substring-after(//Admin/@InsuranceZip, '-'))=4">
<xsl:value-of select="//Admin/@InsuranceZip"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(//Admin/@InsuranceZip, 1, 5)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:when>
<xsl:when test="string-length(//Admin/@InsuranceZip)=9">
<xsl:value-of select="substring(//Admin/@InsuranceZip, 1, 5)"/>-<xsl:value-of select="substring(//Admin/@InsuranceZip, 6, 4)"/>
</xsl:when>
<xsl:when test="string-length(//Admin/@InsuranceZip)&gt;4">
<xsl:value-of select="substring(//Admin/@InsuranceZip, 1, 5)"/>
</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Country">
<xsl:value-of select="//Admin/@InsuranceCountry"/>
</xsl:attribute>
<xsl:attribute name="Phone">
<xsl:choose>
<xsl:when test="//Admin/@InsurancePhone1Ext[. != '']">
<xsl:value-of select="//Admin/@InsurancePhone1"/>&#32;x<xsl:value-of select="//Admin/@InsurancePhone1Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@InsurancePhone1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Phone2">
<xsl:choose>
<xsl:when test="//Admin/@InsurancePhone2Ext[. != '']">
<xsl:value-of select="//Admin/@InsurancePhone2"/>&#32;x<xsl:value-of select="//Admin/@InsurancePhone2Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@InsurancePhone2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Fax">
<xsl:choose>
<xsl:when test="//Admin/@InsuranceFaxExt[. != '']">
<xsl:value-of select="//Admin/@InsuranceFax"/>&#32;x<xsl:value-of select="//Admin/@InsuranceFaxExt"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@InsuranceFax"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Email">
<xsl:value-of select="//Admin/@InsuranceEmail"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="Insured">
<xsl:attribute name="LastName">
<xsl:value-of select="//Admin/@InsuranceLName" />
</xsl:attribute>
</xsl:element>
<xsl:element name="Claim">
<xsl:attribute name="Number">
<xsl:value-of select="//Admin/@ClaimNumber"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="Estimator">
<xsl:attribute name="FName">
<xsl:value-of select="//Admin2/@EstimatorF"/>
</xsl:attribute>
<xsl:attribute name="LName">
<xsl:value-of select="//Admin2/@EstimatorL"/>
</xsl:attribute>
<xsl:attribute name="Company">
<xsl:value-of select="//Admin2/@EstimatorCompanyName"/>
</xsl:attribute>
<xsl:attribute name="Address">
<xsl:value-of select="//Admin2/@EstimatorAddr1"/>
</xsl:attribute>
<xsl:attribute name="Address2">
<xsl:value-of select="//Admin2/@EstimatorAddr2"/>
</xsl:attribute>
<xsl:attribute name="City">
<xsl:value-of select="//Admin2/@EstimatorCity"/>
</xsl:attribute>
<xsl:attribute name="State">
<xsl:value-of select="//Admin2/@EstimatorState"/>
</xsl:attribute>
<xsl:attribute name="Zip">
<xsl:choose>
<xsl:when test="contains(//Admin2/@EstimatorZip, '-')">
<xsl:choose>
<xsl:when test="string-length(substring-before(//Admin2/@EstimatorZip, '-'))=5">
<xsl:choose>
<xsl:when test="string-length(substring-after(//Admin2/@EstimatorZip, '-'))=4">
<xsl:value-of select="//Admin2/@EstimatorZip"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(//Admin2/@EstimatorZip, 1, 5)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:when>
<xsl:when test="string-length(//Admin2/@EstimatorZip)=9">
<xsl:value-of select="substring(//Admin2/@EstimatorZip, 1, 5)"/>-<xsl:value-of select="substring(//Admin2/@EstimatorZip, 6, 4)"/>
</xsl:when>
<xsl:when test="string-length(//Admin2/@EstimatorZip)&gt;4">
<xsl:value-of select="substring(//Admin2/@EstimatorZip, 1, 5)"/>
</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Country">
<xsl:value-of select="//Admin2/@EstimatorCountry"/>
</xsl:attribute>
<xsl:attribute name="Phone">
<xsl:choose>
<xsl:when test="//Admin2/@EstimatorPhone1Ext[. != '']">
<xsl:value-of select="//Admin2/@EstimatorPhone1"/>&#32;x<xsl:value-of select="//Admin2/@EstimatorPhone1Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin2/@EstimatorPhone1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Phone2">
<xsl:choose>
<xsl:when test="//Admin2/@EstimatorPhone2Ext[. != '']">
<xsl:value-of select="//Admin2/@EstimatorPhone2"/>&#32;x<xsl:value-of select="//Admin2/@EstimatorPhone2Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin2/@EstimatorPhone2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Fax">
<xsl:choose>
<xsl:when test="//Admin2/@EstimatorFaxExt[. != '']">
<xsl:value-of select="//Admin2/@EstimatorFax"/>&#32;x<xsl:value-of select="//Admin2/@EstimatorFaxExt"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin2/@EstimatorFax"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Email">
<xsl:value-of select="//Admin2/@EstimatorEmail"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="RepairInformation">
<xsl:attribute name="VehicleDateIn">
<xsl:if test="string-length(//Admin2/@VehicleInDate)&gt;6">
<xsl:value-of select="substring-before(//Admin2/@VehicleInDate, ' ')"/>
<xsl:if test="string-length(//Admin2/@VehicleInTime)=4">
&#32;<xsl:value-of select="substring(//Admin2/@VehicleInTime, 1, 2)"/>:<xsl:value-of select="substring(//Admin2/@VehicleInTime, 3, 2)"/>
</xsl:if>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="EstimatedVehicleDateOut">
<xsl:if test="string-length(//Admin2/@TargetVehicleOutDate)&gt;6">
<xsl:value-of select="substring-before(//Admin2/@TargetVehicleOutDate,' ')"/>
<xsl:if test="string-length(//Admin2/@TargetVehicleOutTime)=4">
&#32;<xsl:value-of select="substring(//Admin2/@TargetVehicleOutTime, 1, 2)"/>:<xsl:value-of select="substring(//Admin2/@TargetVehicleOutTime, 3, 2)"/>
</xsl:if>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="VehicleDateOut">
<xsl:if test="string-length(//Admin2/@VehicleOutDate)&gt;6">
<xsl:value-of select="substring-before(//Admin2/@VehicleOutDate, ' ')"/>
<xsl:if test="string-length(//Admin2/@VehicleOutTime)=4">
&#32;<xsl:value-of select="substring(//Admin2/@VehicleOutTime, 1, 2)"/>:<xsl:value-of select="substring(//Admin2/@VehicleOutTime, 3, 2)"/>
</xsl:if>
</xsl:if>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template match="PartsList">
<xsl:element name="PartsList" >
<xsl:for-each select="Part">
<xsl:element name="Part" >
<!-- Part number translation rules -->
<xsl:variable name="OEMPartNumber">
<xsl:if test="substring(@TDPartNum, string-length(@TDPartNum) - 7) != ' GM PART'">
<xsl:value-of select="@TDPartNum" />
</xsl:if>
<xsl:if test="substring(@TDPartNum, string-length(@TDPartNum) - 7) = ' GM PART'">
<xsl:value-of select="substring-before(@TDPartNum,' GM PART')" />
</xsl:if>
</xsl:variable>
<xsl:variable name="AltPartNumber">
<xsl:value-of select="@AltPartNum"/>
</xsl:variable>
<xsl:variable name="PrimaryPartType">
<!-- RoseP modified to convert PAP to PAN -->
<!--<xsl:value-of select="@PartType"/>-->
<xsl:choose>
<xsl:when test="@PartType='PAP'">PAN</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@PartType"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="PrimaryPartNumber">
<xsl:choose>
<xsl:when test="@PartType='PAN'">
<xsl:value-of select="$OEMPartNumber"/>
</xsl:when>
<xsl:when test="@PartType='PAA'">
<xsl:value-of select="$AltPartNumber"/>
</xsl:when>
<xsl:when test="@PartType='PAL'">
<xsl:choose>
<xsl:when test="$AltPartNumber!=''">
<xsl:value-of select="$AltPartNumber"/>
</xsl:when>
<xsl:when test="$OEMPartNumber!=''">
<xsl:value-of select="$OEMPartNumber"/>
</xsl:when>
<xsl:otherwise>Salvage or Assembly</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="@PartType='PAG'">
<xsl:value-of select="$OEMPartNumber"/>
</xsl:when>
<!-- RoseP added Dec 08, 06 -->
<xsl:when test="@PartType='PAP'">
<xsl:value-of select="$OEMPartNumber"/>
</xsl:when>
<xsl:when test="@PartType='PAM'">
<xsl:choose>
<xsl:when test="$AltPartNumber!=''">
<xsl:value-of select="$AltPartNumber"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$OEMPartNumber"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="SecondaryPartNumber">
<xsl:choose>
<xsl:when test="@PartType='PAA'">
<xsl:value-of select="$OEMPartNumber"/>
</xsl:when>
<xsl:when test="@PartType='PAM'">
<xsl:value-of select="$OEMPartNumber"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="SecondaryPartType">
<xsl:choose>
<xsl:when test="@PartType='PAA'">PAN</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:attribute name="TDPartNum">
<xsl:value-of select="$PrimaryPartNumber"/>
</xsl:attribute>
<xsl:attribute name="AltPartNum">
<xsl:value-of select="$SecondaryPartNumber" />
</xsl:attribute>
<xsl:attribute name="ExternalPartType">
<xsl:value-of select="$PrimaryPartType" />
</xsl:attribute>
<xsl:attribute name="ExternalAltPartType">
<xsl:value-of select="$SecondaryPartType" />
</xsl:attribute>
<xsl:attribute name="TDPartType">
<xsl:choose>
<xsl:when test="$PrimaryPartType='PAN'">1</xsl:when>
<xsl:when test="$PrimaryPartType='PAA'">2</xsl:when>
<xsl:when test="$PrimaryPartType='PAL'">3</xsl:when>
<xsl:when test="$PrimaryPartType='PAM'">4</xsl:when>
<xsl:when test="$PrimaryPartType='PAR'">4</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="AltPartType">
<xsl:choose>
<xsl:when test="$SecondaryPartType='PAN'">1</xsl:when>
<xsl:when test="$SecondaryPartType='PAA'">2</xsl:when>
<xsl:when test="$SecondaryPartType='PAL'">3</xsl:when>
<xsl:when test="$SecondaryPartType='PAM'">4</xsl:when>
<xsl:when test="$SecondaryPartType='PAR'">4</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="TDPartDesc">
<xsl:value-of select="@TDPartDesc" />
</xsl:attribute>
<xsl:attribute name="TDLineRef">
<xsl:value-of select="@TDLineRef" />
</xsl:attribute>
<xsl:attribute name="TDEstimate">
<xsl:choose>
<xsl:when test="@PartType='PAN'">
<xsl:value-of select="@TDEstimate" />
</xsl:when>
<xsl:when test="@PartType='PAM'">
<xsl:value-of select="@ActPrice" />
</xsl:when>
<xsl:when test="@PartType='PAA'">
<xsl:value-of select="@ActPrice" />
</xsl:when>
<xsl:when test="@PartType='PAL'">
<xsl:value-of select="@ActPrice" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@ActPrice" />
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="TDPartQty">
<xsl:value-of select="@TDPartQty" />
</xsl:attribute>
<xsl:attribute name="LineNumber">
<xsl:value-of select="@LineNumber" />
</xsl:attribute>
<xsl:attribute name="SequenceNumber">
<xsl:value-of select="@SequenceNumber" />
</xsl:attribute>
<xsl:attribute name="SupplLevel">
<xsl:choose>
<xsl:when test="@SupplementLevel = 'E01'">0</xsl:when>
<xsl:when test="substring(@SupplementLevel, 1, 1) = 'S'">
<xsl:value-of select="substring(@SupplementLevel, 2)" />
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="LaborType">
<xsl:value-of select="@LaborType" />
</xsl:attribute>
<xsl:attribute name="LaborHours">
<xsl:value-of select="@LaborHours" />
</xsl:attribute>
<xsl:attribute name="OperationCode">
<xsl:value-of select="@LaborOp" />
</xsl:attribute>
<xsl:attribute name="CLPart">
<!-- CLPart is false if TRAN_CODE == 2 or TRAN_CODE == 3 -->
<!-- CLPart is false if PartType is not 'PAN','PAP','PAL','PAG', 'PAM', 'PAA', 'PAO', 'PAR', or '' -->
<!-- CLPart is false if LaborCode is only labor -->
<!-- CLPart is false if OpCode is not "Remove/Replace" or "Remove/Replace Partial" or "Repair, Partial" -->
<xsl:choose>
<xsl:when test="@TransactionCode='3'">False</xsl:when>
<xsl:when test="@TransactionCode='1' or @TransactionCode ='2'">
<xsl:choose>
<xsl:when test="@PartType='PAN' or @PartType='PAG' or @PartType='PAM' or @PartType='PAP' or @PartType='PAL' or @PartType='PAA' or @PartType='PAO' or @PartType='PAR' or @PartType=''" >
<!-- we now handle blank part types-->
<xsl:choose>
<xsl:when test="@LaborType='LAD' or @LaborType='LAE' or @LaborType='LAU' or @LaborType='LAT'">False</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="@LaborOp=''">False</xsl:when>
<!--Needed for Blank Part Type-->
<xsl:when test="@LaborOp='OP0'">False</xsl:when>
<xsl:when test="@LaborOp='OP1'">False</xsl:when>
<xsl:when test="@LaborOp='OP2'">False</xsl:when>
<xsl:when test="@LaborOp='OP3'">False</xsl:when>
<xsl:when test="@LaborOp='OP4'">False</xsl:when>
<xsl:when test="@LaborOp='OP5'">False</xsl:when>
<xsl:when test="@LaborOp='OP6'">False</xsl:when>
<xsl:when test="@LaborOp='OP7'">False</xsl:when>
<xsl:when test="@LaborOp='OP8'">False</xsl:when>
<xsl:when test="@LaborOp='OP9'">False</xsl:when>
<!-- RoseP removed OP10 Dec 08, 06 -->
<!-- <xsl:when test="@LaborOp='OP10'">False</xsl:when> -->
<xsl:when test="@LaborOp='OP13'">False</xsl:when>
<xsl:when test="@LaborOp='OP14'">False</xsl:when>
<xsl:when test="@LaborOp='OP15'">False</xsl:when>
<xsl:when test="@LaborOp='OP16'">False</xsl:when>
<xsl:otherwise>True</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>False</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>False</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,129 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TranslationDescriptor>
<FileTranslations FormatName="CCC" ElementName="Estimate" Description="Translation Descriptor for a CCC Estimate, Version 2.0">
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.env">
<DBFFileTranslation ID="ID1" ElementName="Envelope" Description="Envelope Table">
<DBFFieldSpec FieldName="RO_ID" Form="attribute" Name="RONum" FieldType="" Description="RO Number"/>
<DBFFieldSpec FieldName="UNQFILE_ID" Form="attribute" Name="UniqueFileID" FieldType="" Description="Unique File Identifier"/>
<DBFFieldSpec FieldName="ESTFILE_ID" Form="attribute" Name="EstimateFileID" FieldType="" Description="Estimate File Identifier"/>
<DBFFieldSpec FieldName="INCL_ADMIN" Form="attribute" Name="IncludesAdminInfo" FieldType="Boolean" Description="Includes Admin Info Flag"/>
<DBFFieldSpec FieldName="INCL_VEH" Form="attribute" Name="IncludesVehicleInfo" FieldType="Boolean" Description="Includes Vehicle Info Flag"/>
<DBFFieldSpec FieldName="INCL_EST" Form="attribute" Name="IncludesEstimateInfo" FieldType="Boolean" Description="Includes Estimate Info Flag"/>
<DBFFieldSpec FieldName="INCL_PROFL" Form="attribute" Name="IncludesProfileInfo" FieldType="Boolean" Description="Includes Profile Info Flag"/>
<DBFFieldSpec FieldName="INCL_TOTAL" Form="attribute" Name="IncludesTotalsInfo" FieldType="Boolean" Description="Includes Totals Info Flag"/>
<DBFFieldSpec FieldName="INCL_VENDR" Form="attribute" Name="IncludesVendorInfo" FieldType="Boolean" Description="Includes Vendor Info Flag"/>
<DBFFieldSpec FieldName="EMS_VER" Form="attribute" Name="EMSVersion" FieldType="" Description="EMS Version Number"/>
<DBFFieldSpec FieldName="SUPP_NO" Form="attribute" Name="SupplementNum" FieldType="" Description="Supplement Number"/>
<DBFFieldSpec FieldName="TRANS_TYPE" Form="attribute" Name="TransactionType" FieldType="" Description="Transaction Type"/>
<DBFFieldSpec FieldName="EST_SYSTEM" Form="attribute" Name="EstimatingSystem" FieldType="" Description="Estimating System Software"/>
<DBFFieldSpec FieldName="SW_VERSION" Form="attribute" Name="SoftwareVersion" FieldType="" Description="Software Version Identifier"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.LIN">
<DBFFileTranslation ID="ID2" ElementSetName="PartsList" ElementName="Part" Description="Parts List Table">
<DBFFieldSpec FieldName="OEM_PARTNO" Form="attribute" Name="TDPartNum" FieldType="" Description="Part Number"/>
<DBFFieldSpec FieldName="ALT_PARTNO" Form="attribute" Name="AltPartNum" FieldType="" Description="Alternate Part Number"/>
<DBFFieldSpec FieldName="ACT_PRICE" Form="attribute" Name="ActPrice" FieldType="" Description="Actual Part Price"/>
<DBFFieldSpec FieldName="LINE_DESC" Form="attribute" Name="TDPartDesc" FieldType="" Description="Part Description"/>
<DBFFieldSpec FieldName="DB_PRICE" Form="attribute" Name="TDEstimate" FieldType="" Description="DB_Price"/>
<DBFFieldSpec FieldName="PART_QTY" Form="attribute" Name="TDPartQty" FieldType="" Description="PART_QTY"/>
<DBFFieldSpec FieldName="LINE_NO" Form="attribute" Name="LineNumber" FieldType="" Description=""/>
<DBFFieldSpec FieldName="UNQ_SEQ" Form="attribute" Name="SequenceNumber" FieldType="" Description=""/>
<DBFFieldSpec FieldName="PART_TYPE" Form="attribute" Name="PartType" FieldType="" Description="Part Type"/>
<DBFFieldSpec FieldName="MOD_LBR_TY" Form="attribute" Name="LaborType" FieldType="" Description="Labor Type"/>
<DBFFieldSpec FieldName="MOD_LB_HRS" Form="attribute" Name="LaborHours" FieldType="" Description="Labor Hours"/>
<DBFFieldSpec FieldName="LBR_OP" Form="attribute" Name="LaborOp" FieldType="" Description="Labor Op"/>
<DBFFieldSpec FieldName="LINE_IND" Form="attribute" Name="SupplementLevel" FieldType="" Description="Supplement Level"/>
<DBFFieldSpec FieldName="TRAN_CODE" Form="attribute" Name="TransactionCode" FieldType="" Description="Transaction Code"/>
<DBFFieldSpec FieldName="LINE_REF" Form="attribute" Name="TDLineRef" FieldType="" Description="Line Reference Number"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.VEH">
<DBFFileTranslation ID="ID3" ElementName="Vehicle" Description="Vehicle Table">
<DBFFieldSpec FieldName="V_VIN" Form="attribute" Name="TransVIN" FieldType="" Description="V_VIN"/>
<DBFFieldSpec FieldName="V_MODEL_YR" Form="attribute" Name="TransYear" FieldType="" Description="V_MODEL_YR"/>
<DBFFieldSpec FieldName="V_MAKEDESC" Form="attribute" Name="ManufName" FieldType="" Description="V_MAKEDESC"/>
<DBFFieldSpec FieldName="V_MAKECODE" Form="attribute" Name="ManufCode" FieldType="" Description="V_MAKECODE"/>
<DBFFieldSpec FieldName="V_MODEL" Form="attribute" Name="TransModel" FieldType="" Description="V_MODEL"/>
<DBFFieldSpec FieldName="V_MILEAGE" Form="attribute" Name="TransMileage" FieldType="" Description="V_MILEAGE"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.AD1">
<DBFFileTranslation ID="ID4" ElementName="Admin" Description="Administrative information">
<DBFFieldSpec FieldName="OWNR_LN" Form="attribute" Name="OwnerL" FieldType="" Description="Owner Lastname"/>
<DBFFieldSpec FieldName="OWNR_FN" Form="attribute" Name="OwnerF" FieldType="" Description="Owner Firstname"/>
<DBFFieldSpec FieldName="OWNR_CO_NM" Form="attribute" Name="OwnerCompanyName" FieldType="" Description="Owner Company Name"/>
<DBFFieldSpec FieldName="OWNR_TITLE" Form="attribute" Name="OwnerTitle" FieldType="" Description="Owner Title"/>
<DBFFieldSpec FieldName="OWNR_ADDR1" Form="attribute" Name="OwnerAddr1" FieldType="" Description="Owner Address Line 1"/>
<DBFFieldSpec FieldName="OWNR_ADDR2" Form="attribute" Name="OwnerAddr2" FieldType="" Description="Owner Address Line 2"/>
<DBFFieldSpec FieldName="OWNR_CITY" Form="attribute" Name="OwnerCity" FieldType="" Description="Owner City"/>
<DBFFieldSpec FieldName="OWNR_ST" Form="attribute" Name="OwnerState" FieldType="" Description="Owner State"/>
<DBFFieldSpec FieldName="OWNR_ZIP" Form="attribute" Name="OwnerZip" FieldType="" Description="Owner Zip"/>
<DBFFieldSpec FieldName="OWNR_CTRY" Form="attribute" Name="OwnerCountry" FieldType="" Description="Owner Country"/>
<DBFFieldSpec FieldName="OWNR_PH1" Form="attribute" Name="OwnerPhone1" FieldType="" Description="Owner Primary Phone"/>
<DBFFieldSpec FieldName="OWNR_PH1X" Form="attribute" Name="OwnerPhone1Ext" FieldType="" Description="Owner Primary Phone Extension"/>
<DBFFieldSpec FieldName="OWNR_PH2" Form="attribute" Name="OwnerPhone2" FieldType="" Description="Owner Secondary Phone"/>
<DBFFieldSpec FieldName="OWNR_PH2X" Form="attribute" Name="OwnerPhone2Ext" FieldType="" Description="Owner Secondary Phone Extension"/>
<DBFFieldSpec FieldName="OWNR_FAX" Form="attribute" Name="OwnerFax" FieldType="" Description="Owner Fax"/>
<DBFFieldSpec FieldName="OWNR_FAXX" Form="attribute" Name="OwnerFaxExt" FieldType="" Description="Owner Fax Extension"/>
<DBFFieldSpec FieldName="OWNR_EA" Form="attribute" Name="OwnerEmail" FieldType="" Description="Owner Email Address"/>
<DBFFieldSpec FieldName="INS_CO_NM" Form="attribute" Name="InsuranceCompanyName" FieldType="" Description="Insurance Company Name"/>
<DBFFieldSpec FieldName="INS_CO_ID" Form="attribute" Name="InsuranceCompanyID" FieldType="" Description="Insurance Company Identifier"/>
<DBFFieldSpec FieldName="INS_ADDR1" Form="attribute" Name="InsuranceAddr1" FieldType="" Description="Insurance Address Line 1"/>
<DBFFieldSpec FieldName="INS_ADDR2" Form="attribute" Name="InsuranceAddr2" FieldType="" Description="Insurance Address Line 2"/>
<DBFFieldSpec FieldName="INS_CITY" Form="attribute" Name="InsuranceCity" FieldType="" Description="Insurance City"/>
<DBFFieldSpec FieldName="INS_ST" Form="attribute" Name="InsuranceState" FieldType="" Description="Insurance State"/>
<DBFFieldSpec FieldName="INS_ZIP" Form="attribute" Name="InsuranceZip" FieldType="" Description="Insurance Zip"/>
<DBFFieldSpec FieldName="INS_CTRY" Form="attribute" Name="InsuranceCountry" FieldType="" Description="Insurance Country"/>
<DBFFieldSpec FieldName="INS_PH1" Form="attribute" Name="InsurancePhone1" FieldType="" Description="Insurance Primary Phone"/>
<DBFFieldSpec FieldName="INS_PH1X" Form="attribute" Name="InsurancePhone1Ext" FieldType="" Description="Insurance Primary Phone Extension"/>
<DBFFieldSpec FieldName="INS_PH2" Form="attribute" Name="InsurancePhone2" FieldType="" Description="Insurance Secondary Phone"/>
<DBFFieldSpec FieldName="INS_PH2X" Form="attribute" Name="InsurancePhone2Ext" FieldType="" Description="Insurance Secondary Phone Extension"/>
<DBFFieldSpec FieldName="INS_FAX" Form="attribute" Name="InsuranceFax" FieldType="" Description="Insurance Fax"/>
<DBFFieldSpec FieldName="INS_FAXX" Form="attribute" Name="InsuranceFaxExt" FieldType="" Description="Insurance Fax Extension"/>
<DBFFieldSpec FieldName="INS_EA" Form="attribute" Name="InsuranceEmail" FieldType="" Description="Insurance Email Address"/>
<DBFFieldSpec FieldName="INSD_LN" Form="attribute" Name="InsuranceLName" FieldType="" Description="Insurance Last Name"/>
<DBFFieldSpec FieldName="CLM_NO" Form="attribute" Name="ClaimNumber" FieldType="" Description="Claim Number"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.AD2">
<DBFFileTranslation ID="ID5" ElementName="Admin2" Description="Administrative information2">
<DBFFieldSpec FieldName="EST_CT_LN" Form="attribute" Name="EstimatorL" FieldType="" Description="Estimator Lastname"/>
<DBFFieldSpec FieldName="EST_CT_FN" Form="attribute" Name="EstimatorF" FieldType="" Description="Estimator Firstname"/>
<DBFFieldSpec FieldName="EST_CO_NM" Form="attribute" Name="EstimatorCompanyName" FieldType="" Description="Estimator Company Name"/>
<DBFFieldSpec FieldName="EST_CO_ID" Form="attribute" Name="EstimatorCompanyID" FieldType="" Description="Estimator Company Identifier"/>
<DBFFieldSpec FieldName="EST_ADDR1" Form="attribute" Name="EstimatorAddr1" FieldType="" Description="Estimator Address1"/>
<DBFFieldSpec FieldName="EST_ADDR2" Form="attribute" Name="EstimatorAddr2" FieldType="" Description="Estimator Address2"/>
<DBFFieldSpec FieldName="EST_CITY" Form="attribute" Name="EstimatorCity" FieldType="" Description="Estimator City"/>
<DBFFieldSpec FieldName="EST_ST" Form="attribute" Name="EstimatorState" FieldType="" Description="Estimator State"/>
<DBFFieldSpec FieldName="EST_ZIP" Form="attribute" Name="EstimatorZip" FieldType="" Description="Estimator Zip"/>
<DBFFieldSpec FieldName="EST_CTRY" Form="attribute" Name="EstimatorCountry" FieldType="" Description="Estimator Country"/>
<DBFFieldSpec FieldName="EST_PH1" Form="attribute" Name="EstimatorPhone1" FieldType="" Description="Estimator Primary Phone"/>
<DBFFieldSpec FieldName="EST_PH1X" Form="attribute" Name="EstimatorPhone1Ext" FieldType="" Description="Estimator Primary Phone Extension"/>
<DBFFieldSpec FieldName="EST_PH2" Form="attribute" Name="EstimatorPhone2" FieldType="" Description="Estimator Secondary Phone"/>
<DBFFieldSpec FieldName="EST_PH2X" Form="attribute" Name="EstimatorPhone2Ext" FieldType="" Description="Estimator Secondary Phone Extension"/>
<DBFFieldSpec FieldName="EST_FAX" Form="attribute" Name="EstimatorFax" FieldType="" Description="Estimator Fax"/>
<DBFFieldSpec FieldName="EST_FAXX" Form="attribute" Name="EstimatorFaxExt" FieldType="" Description="Estimator Fax Extension"/>
<DBFFieldSpec FieldName="EST_EA" Form="attribute" Name="EstimatorEmail" FieldType="" Description="Estimator Email Address"/>
<DBFFieldSpec FieldName="EST_LIC_NO" Form="attribute" Name="EstimatorLicenseNumber" FieldType="" Description="Estimator License Number"/>
<DBFFieldSpec FieldName="EST_FILENO" Form="attribute" Name="EstimatorFileNumber" FieldType="" Description="Estimator File Number"/>
<DBFFieldSpec FieldName="RO_IN_DATE" Form="attribute" Name="VehicleInDate" FieldType="" Description="Date arrived in shop"/>
<DBFFieldSpec FieldName="RO_IN_TIME" Form="attribute" Name="VehicleInTime" FieldType="" Description="Time arrived in shop"/>
<DBFFieldSpec FieldName="TAR_DATE" Form="attribute" Name="TargetVehicleOutDate" FieldType="" Description="Target date to be completed"/>
<DBFFieldSpec FieldName="TAR_TIME" Form="attribute" Name="TargetVehicleOutTime" FieldType="" Description="Target time to be completed"/>
<DBFFieldSpec FieldName="RO_CMPDATE" Form="attribute" Name="VehicleOutDate" FieldType="" Description="Date completed"/>
<DBFFieldSpec FieldName="RO_CMPTIME" Form="attribute" Name="VehicleOutTime" FieldType="" Description="Time completed"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.TTL">
<DBFFileTranslation ID="ID6" ElementName="Total" Description="Total Table">
<DBFFieldSpec FieldName="G_TTL_AMT" Form="attribute" Name="GrandTotalAmount" FieldType="" Description=""/>
</DBFFileTranslation>
</FileTranslation>
</FileTranslations>
<StyleSheetTranslation>
<StyleSheetURL>ccc.xsl</StyleSheetURL>
</StyleSheetTranslation>
</TranslationDescriptor>

View File

@@ -1,580 +0,0 @@
<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<xsl:for-each select="//TranslatedData">
<xsl:element name="TranslatedData">
<xsl:attribute name="TranslationOutputFile">
<xsl:value-of select="@TranslationOutputFile"/>
</xsl:attribute>
<xsl:choose>
<xsl:when test="Details/@PrgID">
<xsl:copy-of select="Details" />
</xsl:when>
<xsl:otherwise>
<xsl:element name="Details">
<xsl:attribute name="PrgID">OECTrans.ImportTrans</xsl:attribute>
<xsl:for-each select="Details/@*">
<xsl:copy-of select="." />
</xsl:for-each>
<xsl:for-each select="Details/*">
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
<xsl:element name="Header">
<xsl:attribute name="RONum">
<xsl:value-of select="//Envelope/@RONum" />
</xsl:attribute>
<xsl:attribute name="OwnerFName">
<xsl:value-of select="//Admin/@OwnerF"/>
</xsl:attribute>
<xsl:attribute name="OwnerLName">
<xsl:value-of select="//Admin/@OwnerL"/>
</xsl:attribute>
<xsl:attribute name="VIN">
<xsl:value-of select="//Vehicle/@TransVIN" />
</xsl:attribute>
<xsl:attribute name="Mileage">
<xsl:value-of select="//Vehicle/@TransMileage" />
</xsl:attribute>
<xsl:attribute name="Year">
<xsl:value-of select="//Vehicle/@TransYear" />
</xsl:attribute>
<xsl:attribute name="Make">
<xsl:if test="//Vehicle/@ManufName[. != '']" >
<xsl:value-of select="//Vehicle/@ManufName" />
</xsl:if>
<xsl:if test="//Vehicle/@ManufName[. = '']" >
<xsl:value-of select="//Vehicle/@ManufCode" />
</xsl:if>
</xsl:attribute>
<xsl:attribute name="Model">
<xsl:value-of select="//Vehicle/@TransModel" />
</xsl:attribute>
<xsl:attribute name="Description">
<xsl:value-of select="//Vehicle/@TransYear" />
<xsl:text> </xsl:text>
<xsl:value-of select="//Vehicle/@ManufName" />
<xsl:text> </xsl:text>
<xsl:value-of select="//Vehicle/@TransModel" />
</xsl:attribute>
<xsl:attribute name="LastSupplLevel">
<xsl:choose>
<xsl:when test="string-length(//Envelope/@SupplementNum) > 0">
<xsl:value-of select="//Envelope/@SupplementNum" />
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:element>
<xsl:apply-templates select="PartsList" />
<xsl:element name="Envelope">
<xsl:element name="Software">
<xsl:attribute name="Manifest">Mitchell.xml</xsl:attribute>
<xsl:attribute name="Descriptor">Mitchell.xsl</xsl:attribute>
<xsl:attribute name="System">
<xsl:value-of select="//Envelope/@EstimatingSystem"/>
</xsl:attribute>
<xsl:attribute name="Version">
<xsl:value-of select="//Envelope/@SoftwareVersion"/>
</xsl:attribute>
<xsl:attribute name="SystemName">
<xsl:value-of select="//Envelope/@SystemName"/>
</xsl:attribute>
<xsl:attribute name="EstimateFileID">
<xsl:value-of select="//Envelope/@EstimateFileID"/>
</xsl:attribute>
<xsl:attribute name="UniqueFileID">
<xsl:value-of select="//Envelope/@UniqueFileID"/>
</xsl:attribute>
<xsl:attribute name="EstimateID">
<xsl:value-of select="//Envelope/@EstimatingSystem"/>-<xsl:value-of select="//Envelope/@SoftwareVersion"/>-<xsl:value-of select="//Envelope/@EstimateFileID"/>
</xsl:attribute>
<xsl:attribute name="EstimateIDv2">
<xsl:value-of select="//Envelope/@EstimatingSystem"/>-<xsl:value-of select="//Envelope/@EstimateFileID"/>
</xsl:attribute>
<xsl:attribute name="EstimateFormatVersion">
<xsl:value-of select="//Envelope/@EMSVersion"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
<xsl:element name="Totals">
<xsl:attribute name="GrandTotalAmount">
<xsl:value-of select="//Total/@GrandTotalAmount"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="Administrative">
<xsl:element name="Owner">
<xsl:attribute name="FName">
<xsl:value-of select="//Admin/@OwnerF"/>
</xsl:attribute>
<xsl:attribute name="LName">
<xsl:value-of select="//Admin/@OwnerL"/>
</xsl:attribute>
<xsl:attribute name="Company">
<!--<xsl:value-of select="//Admin/@OwnerCompanyName"/>-->
</xsl:attribute>
<xsl:attribute name="Address">
<!--<xsl:value-of select="//Admin/@OwnerAddr1"/>-->
</xsl:attribute>
<xsl:attribute name="Address2">
<!--<xsl:value-of select="//Admin/@OwnerAddr2"/>-->
</xsl:attribute>
<xsl:attribute name="City">
<!--<xsl:value-of select="//Admin/@OwnerCity"/>-->
</xsl:attribute>
<xsl:attribute name="State">
<xsl:value-of select="//Admin/@OwnerState"/>
</xsl:attribute>
<xsl:attribute name="Zip">
<!--<xsl:choose>
<xsl:when test="contains(//Admin/@OwnerZip, '-')">
<xsl:choose>
<xsl:when test="string-length(substring-before(//Admin/@OwnerZip, '-'))=5">
<xsl:choose>
<xsl:when test="string-length(substring-after(//Admin/@OwnerZip, '-'))=4">
<xsl:value-of select="//Admin/@OwnerZip"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(//Admin/@OwnerZip, 1, 5)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:when>
<xsl:when test="string-length(//Admin/@OwnerZip)=9">
<xsl:value-of select="substring(//Admin/@OwnerZip, 1, 5)"/>-<xsl:value-of select="substring(//Admin/@OwnerZip, 6, 4)"/>
</xsl:when>
<xsl:when test="string-length(//Admin/@OwnerZip)&gt;4">
<xsl:value-of select="substring(//Admin/@OwnerZip, 1, 5)"/>
</xsl:when>
</xsl:choose>-->
</xsl:attribute>
<xsl:attribute name="Country">
<!--<xsl:value-of select="//Admin/@OwnerCountry"/>-->
</xsl:attribute>
<xsl:attribute name="Phone">
<!--<xsl:choose>
<xsl:when test="//Admin/@OwnerPhone1Ext[. != '']">
<xsl:value-of select="//Admin/@OwnerPhone1"/>&#32;x<xsl:value-of select="//Admin/@OwnerPhone1Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@OwnerPhone1"/>
</xsl:otherwise>
</xsl:choose>-->
</xsl:attribute>
<xsl:attribute name="Phone2">
<!--<xsl:choose>
<xsl:when test="//Admin/@OwnerPhone2Ext[. != '']">
<xsl:value-of select="//Admin/@OwnerPhone2"/>&#32;x<xsl:value-of select="//Admin/@OwnerPhone2Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@OwnerPhone2"/>
</xsl:otherwise>
</xsl:choose>-->
</xsl:attribute>
<xsl:attribute name="Fax">
<!--<xsl:choose>
<xsl:when test="//Admin/@OwnerFaxExt[. != '']">
<xsl:value-of select="//Admin/@OwnerFax"/>&#32;x<xsl:value-of select="//Admin/@OwnerFaxExt"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@OwnerFax"/>
</xsl:otherwise>
</xsl:choose>-->
</xsl:attribute>
<xsl:attribute name="Email">
<!--<xsl:value-of select="//Admin/@OwnerEmail"/>-->
</xsl:attribute>
</xsl:element>
<xsl:element name="InsuranceCompany">
<xsl:attribute name="Id">
<xsl:value-of select="//Admin/@InsuranceCompanyID"/>
</xsl:attribute>
<xsl:attribute name="Name">
<xsl:value-of select="//Admin/@InsuranceCompanyName"/>
</xsl:attribute>
<xsl:attribute name="Address">
<xsl:value-of select="//Admin/@InsuranceAddr1"/>
</xsl:attribute>
<xsl:attribute name="Address2">
<xsl:value-of select="//Admin/@InsuranceAddr2"/>
</xsl:attribute>
<xsl:attribute name="City">
<xsl:value-of select="//Admin/@InsuranceCity"/>
</xsl:attribute>
<xsl:attribute name="State">
<xsl:value-of select="//Admin/@InsuranceState"/>
</xsl:attribute>
<xsl:attribute name="Zip">
<xsl:choose>
<xsl:when test="contains(//Admin/@InsuranceZip, '-')">
<xsl:choose>
<xsl:when test="string-length(substring-before(//Admin/@InsuranceZip, '-'))=5">
<xsl:choose>
<xsl:when test="string-length(substring-after(//Admin/@InsuranceZip, '-'))=4">
<xsl:value-of select="//Admin/@InsuranceZip"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(//Admin/@InsuranceZip, 1, 5)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:when>
<xsl:when test="string-length(//Admin/@InsuranceZip)=9">
<xsl:value-of select="substring(//Admin/@InsuranceZip, 1, 5)"/>-<xsl:value-of select="substring(//Admin/@InsuranceZip, 6, 4)"/>
</xsl:when>
<xsl:when test="string-length(//Admin/@InsuranceZip)&gt;4">
<xsl:value-of select="substring(//Admin/@InsuranceZip, 1, 5)"/>
</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Country">
<xsl:value-of select="//Admin/@InsuranceCountry"/>
</xsl:attribute>
<xsl:attribute name="Phone">
<xsl:choose>
<xsl:when test="//Admin/@InsurancePhone1Ext[. != '']">
<xsl:value-of select="//Admin/@InsurancePhone1"/>&#32;x<xsl:value-of select="//Admin/@InsurancePhone1Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@InsurancePhone1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Phone2">
<xsl:choose>
<xsl:when test="//Admin/@InsurancePhone2Ext[. != '']">
<xsl:value-of select="//Admin/@InsurancePhone2"/>&#32;x<xsl:value-of select="//Admin/@InsurancePhone2Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@InsurancePhone2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Fax">
<xsl:choose>
<xsl:when test="//Admin/@InsuranceFaxExt[. != '']">
<xsl:value-of select="//Admin/@InsuranceFax"/>&#32;x<xsl:value-of select="//Admin/@InsuranceFaxExt"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin/@InsuranceFax"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Email">
<xsl:value-of select="//Admin/@InsuranceEmail"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="Insured">
<xsl:attribute name="LastName">
<xsl:value-of select="//Admin/@InsuranceLName"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="Claim">
<xsl:attribute name="Number">
<xsl:value-of select="//Admin/@ClaimNumber"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="Estimator">
<xsl:attribute name="FName">
<xsl:value-of select="//Admin2/@EstimatorF"/>
</xsl:attribute>
<xsl:attribute name="LName">
<xsl:value-of select="//Admin2/@EstimatorL"/>
</xsl:attribute>
<xsl:attribute name="Company">
<xsl:value-of select="//Admin2/@EstimatorCompanyName"/>
</xsl:attribute>
<xsl:attribute name="BodyShopName">
<xsl:value-of select="//Admin2/@BodyShopName"/>
</xsl:attribute>
<xsl:attribute name="Address">
<xsl:value-of select="//Admin2/@EstimatorAddr1"/>
</xsl:attribute>
<xsl:attribute name="Address2">
<xsl:value-of select="//Admin2/@EstimatorAddr2"/>
</xsl:attribute>
<xsl:attribute name="City">
<xsl:value-of select="//Admin2/@EstimatorCity"/>
</xsl:attribute>
<xsl:attribute name="State">
<xsl:value-of select="//Admin2/@EstimatorState"/>
</xsl:attribute>
<xsl:attribute name="Zip">
<xsl:choose>
<xsl:when test="contains(//Admin2/@EstimatorZip, '-')">
<xsl:choose>
<xsl:when test="string-length(substring-before(//Admin2/@EstimatorZip, '-'))=5">
<xsl:choose>
<xsl:when test="string-length(substring-after(//Admin2/@EstimatorZip, '-'))=4">
<xsl:value-of select="//Admin2/@EstimatorZip"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(//Admin2/@EstimatorZip, 1, 5)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:when>
<xsl:when test="string-length(//Admin2/@EstimatorZip)=9">
<xsl:value-of select="substring(//Admin2/@EstimatorZip, 1, 5)"/>-<xsl:value-of select="substring(//Admin2/@EstimatorZip, 6, 4)"/>
</xsl:when>
<xsl:when test="string-length(//Admin2/@EstimatorZip)&gt;4">
<xsl:value-of select="substring(//Admin2/@EstimatorZip, 1, 5)"/>
</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Country">
<xsl:value-of select="//Admin2/@EstimatorCountry"/>
</xsl:attribute>
<xsl:attribute name="Phone">
<xsl:choose>
<xsl:when test="//Admin2/@EstimatorPhone1Ext[. != '']">
<xsl:value-of select="//Admin2/@EstimatorPhone1"/>&#32;x<xsl:value-of select="//Admin2/@EstimatorPhone1Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin2/@EstimatorPhone1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Phone2">
<xsl:choose>
<xsl:when test="//Admin2/@EstimatorPhone2Ext[. != '']">
<xsl:value-of select="//Admin2/@EstimatorPhone2"/>&#32;x<xsl:value-of select="//Admin2/@EstimatorPhone2Ext"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin2/@EstimatorPhone2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Fax">
<xsl:choose>
<xsl:when test="//Admin2/@EstimatorFaxExt[. != '']">
<xsl:value-of select="//Admin2/@EstimatorFax"/>&#32;x<xsl:value-of select="//Admin2/@EstimatorFaxExt"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//Admin2/@EstimatorFax"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="Email">
<xsl:value-of select="//Admin2/@EstimatorEmail"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="RepairInformation">
<xsl:attribute name="VehicleDateIn">
<xsl:if test="string-length(//Admin2/@VehicleInDate)&gt;6">
<xsl:value-of select="substring-before(//Admin2/@VehicleInDate, ' ')"/>
<xsl:if test="string-length(//Admin2/@VehicleInTime)=4">
&#32;<xsl:value-of select="substring(//Admin2/@VehicleInTime, 1, 2)"/>:<xsl:value-of select="substring(//Admin2/@VehicleInTime, 3, 2)"/>
</xsl:if>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="EstimatedVehicleDateOut">
<xsl:if test="string-length(//Admin2/@TargetVehicleOutDate)&gt;6">
<xsl:value-of select="substring-before(//Admin2/@TargetVehicleOutDate,' ')"/>
<xsl:if test="string-length(//Admin2/@TargetVehicleOutTime)=4">
&#32;<xsl:value-of select="substring(//Admin2/@TargetVehicleOutTime, 1, 2)"/>:<xsl:value-of select="substring(//Admin2/@TargetVehicleOutTime, 3, 2)"/>
</xsl:if>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="VehicleDateOut">
<xsl:if test="string-length(//Admin2/@VehicleOutDate)&gt;6">
<xsl:value-of select="substring-before(//Admin2/@VehicleOutDate, ' ')"/>
<xsl:if test="string-length(//Admin2/@VehicleOutTime)=4">
&#32;<xsl:value-of select="substring(//Admin2/@VehicleOutTime, 1, 2)"/>:<xsl:value-of select="substring(//Admin2/@VehicleOutTime, 3, 2)"/>
</xsl:if>
</xsl:if>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template match="PartsList">
<xsl:element name="PartsList" >
<xsl:for-each select="Part">
<xsl:element name="Part" >
<!-- Part number translation rules -->
<xsl:variable name="OEMPartNumber">
<xsl:if test="substring(@TDPartNum, string-length(@TDPartNum) - 7) != ' GM PART'">
<xsl:value-of select="@TDPartNum" />
</xsl:if>
<xsl:if test="substring(@TDPartNum, string-length(@TDPartNum) - 7) = ' GM PART'">
<xsl:value-of select="substring-before(@TDPartNum,' GM PART')" />
</xsl:if>
</xsl:variable>
<xsl:variable name="AltPartNumber">
<xsl:value-of select="@AltPartNum"/>
</xsl:variable>
<xsl:variable name="PrimaryPartType">
<xsl:value-of select="@PartType"/>
</xsl:variable>
<xsl:variable name="PrimaryPartNumber">
<xsl:choose>
<xsl:when test="@PartType='PAN'">
<xsl:value-of select="$OEMPartNumber"/>
</xsl:when>
<xsl:when test="@PartType!='PAN'">
<xsl:choose>
<xsl:when test="$AltPartNumber!=''">
<xsl:value-of select="$AltPartNumber"/>
</xsl:when>
<xsl:when test="$OEMPartNumber!=''">
<xsl:value-of select="$OEMPartNumber"/>
</xsl:when>
<xsl:otherwise>Salvage or Assembly</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="SecondaryPartNumber">
<xsl:choose>
<xsl:when test="@PartType!='PAN'">
<xsl:value-of select="$OEMPartNumber"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="SecondaryPartType">
<xsl:choose>
<xsl:when test="@PartType='PAA'">PAN</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:attribute name="TDPartNum">
<xsl:value-of select="$PrimaryPartNumber"/>
</xsl:attribute>
<xsl:attribute name="AltPartNum">
<xsl:value-of select="$SecondaryPartNumber" />
</xsl:attribute>
<xsl:attribute name="ExternalPartType">
<xsl:value-of select="$PrimaryPartType" />
</xsl:attribute>
<xsl:attribute name="ExternalAltPartType">
<xsl:value-of select="$SecondaryPartType" />
</xsl:attribute>
<xsl:attribute name="TDPartType">
<xsl:choose>
<xsl:when test="$PrimaryPartType='PAN'">1</xsl:when>
<xsl:when test="$PrimaryPartType='PAA'">2</xsl:when>
<xsl:when test="$PrimaryPartType='PAL'">3</xsl:when>
<xsl:when test="$PrimaryPartType='PAM'">4</xsl:when>
<xsl:when test="$PrimaryPartType='PAR'">4</xsl:when>
<xsl:when test="$PrimaryPartType='PAC'">4</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="AltPartType">
<xsl:choose>
<xsl:when test="$SecondaryPartType='PAN'">1</xsl:when>
<xsl:when test="$SecondaryPartType='PAA'">2</xsl:when>
<xsl:when test="$SecondaryPartType='PAL'">3</xsl:when>
<xsl:when test="$SecondaryPartType='PAM'">4</xsl:when>
<xsl:when test="$SecondaryPartType='PAC'">4</xsl:when>
<xsl:when test="$SecondaryPartType='PAR'">4</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="TDPartDesc">
<xsl:value-of select="@TDPartDesc" />
</xsl:attribute>
<xsl:attribute name="TDEstimate">
<xsl:choose>
<xsl:when test="@PartType='PAN'">
<xsl:value-of select="@TDEstimate" />
</xsl:when>
<xsl:when test="@PartType='PAM'">
<xsl:value-of select="@ActPrice" />
</xsl:when>
<xsl:when test="@PartType='PAA'">
<xsl:value-of select="@ActPrice" />
</xsl:when>
<xsl:when test="@PartType='PAL'">
<xsl:value-of select="@ActPrice" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@ActPrice" />
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="TDPartQty">
<xsl:value-of select="@TDPartQty" />
</xsl:attribute>
<xsl:attribute name="LineNumber">
<xsl:value-of select="@LineNumber" />
</xsl:attribute>
<xsl:attribute name="SequenceNumber">
<xsl:value-of select="@SequenceNumber" />
</xsl:attribute>
<xsl:attribute name="SupplLevel">
<xsl:choose>
<xsl:when test="@SupplementLevel = 'E'">0</xsl:when>
<xsl:when test="substring(@SupplementLevel, 1, 1) = 'S'">
<xsl:value-of select="substring(@SupplementLevel, 2)" />
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="LaborType">
<xsl:value-of select="@LaborType" />
</xsl:attribute>
<xsl:attribute name="LaborHours">
<xsl:value-of select="@LaborHours" />
</xsl:attribute>
<xsl:attribute name="OperationCode">
<xsl:value-of select="@LaborOp" />
</xsl:attribute>
<xsl:attribute name="GlassFlag">
<xsl:value-of select="@GlassFlag" />
</xsl:attribute>
<xsl:attribute name="MarkUp">
<xsl:value-of select="@MarkUp" />
</xsl:attribute>
<xsl:attribute name="CLPart">
<!-- CLPart is false if TRAN_CODE == 2 or TRAN_CODE == 3 -->
<!-- CLPart is false if PartType is not 'PAN','PAP','PAL','PAG', 'PAM', or 'PAA' or 'PAC' or 'PAR' -->
<!-- CLPart is false if LaborCode is only labor -->
<!-- CLPart is false if OpCode is not "Remove/Replace" or "Remove/Replace Partial" -->
<xsl:choose>
<xsl:when test="@TransactionCode='3'">False</xsl:when>
<xsl:when test="@TransactionCode='1' or @TransactionCode='2'">
<xsl:choose>
<xsl:when test="@PartType='PAN' or @PartType='PAG' or @PartType='PAM' or @PartType='PAP' or @PartType='PAL' or @PartType='PAA' or @PartType='PAR' or @PartType='PAC'">
<xsl:choose>
<xsl:when test="@LaborType='LAD' or @LaborType='LAE' or @LaborType='LAU' or @LaborType='LAT'">False</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="@LaborOp='OP0'">False</xsl:when>
<xsl:when test="@LaborOp='OP1'">False</xsl:when>
<xsl:when test="@LaborOp='OP2'">False</xsl:when>
<xsl:when test="@LaborOp='OP3'">False</xsl:when>
<xsl:when test="@LaborOp='OP4'">False</xsl:when>
<xsl:when test="@LaborOp='OP5'">False</xsl:when>
<xsl:when test="@LaborOp='OP6'">False</xsl:when>
<xsl:when test="@LaborOp='OP7'">False</xsl:when>
<xsl:when test="@LaborOp='OP8'">False</xsl:when>
<xsl:when test="@LaborOp='OP9'">False</xsl:when>
<xsl:when test="@LaborOp='OP10'">False</xsl:when>
<xsl:when test="@LaborOp='OP13'">False</xsl:when>
<xsl:when test="@LaborOp='OP14'">False</xsl:when>
<xsl:when test="@LaborOp='OP15'">False</xsl:when>
<xsl:when test="@LaborOp='OP16'">False</xsl:when>
<xsl:otherwise>True</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>False</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>False</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,132 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TranslationDescriptor>
<FileTranslations FormatName="Mitchell" ElementName="Estimate" Description="Translation Descriptor for a Mitchell Estimate, Version EMS 2.0">
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.env">
<DBFFileTranslation ID="ID1" ElementName="Envelope" Description="Envelope Table">
<DBFFieldSpec FieldName="RO_ID" Form="attribute" Name="RONum" FieldType="" Description="RO Number"/>
<DBFFieldSpec FieldName="UNQFILE_ID" Form="attribute" Name="UniqueFileID" FieldType="" Description="Unique File Identifier"/>
<DBFFieldSpec FieldName="ESTFILE_ID" Form="attribute" Name="EstimateFileID" FieldType="" Description="Estimate File Identifier"/>
<DBFFieldSpec FieldName="INCL_ADMIN" Form="attribute" Name="IncludesAdminInfo" FieldType="Boolean" Description="Includes Admin Info Flag"/>
<DBFFieldSpec FieldName="INCL_VEH" Form="attribute" Name="IncludesVehicleInfo" FieldType="Boolean" Description="Includes Vehicle Info Flag"/>
<DBFFieldSpec FieldName="INCL_EST" Form="attribute" Name="IncludesEstimateInfo" FieldType="Boolean" Description="Includes Estimate Info Flag"/>
<DBFFieldSpec FieldName="INCL_PROFL" Form="attribute" Name="IncludesProfileInfo" FieldType="Boolean" Description="Includes Profile Info Flag"/>
<DBFFieldSpec FieldName="INCL_TOTAL" Form="attribute" Name="IncludesTotalsInfo" FieldType="Boolean" Description="Includes Totals Info Flag"/>
<DBFFieldSpec FieldName="INCL_VENDR" Form="attribute" Name="IncludesVendorInfo" FieldType="Boolean" Description="Includes Vendor Info Flag"/>
<DBFFieldSpec FieldName="EMS_VER" Form="attribute" Name="EMSVersion" FieldType="" Description="EMS Version Number"/>
<DBFFieldSpec FieldName="SUPP_NO" Form="attribute" Name="SupplementNum" FieldType="" Description="Supplement Number"/>
<DBFFieldSpec FieldName="TRANS_TYPE" Form="attribute" Name="TransactionType" FieldType="" Description="Transaction Type"/>
<DBFFieldSpec FieldName="EST_SYSTEM" Form="attribute" Name="EstimatingSystem" FieldType="" Description="Estimating System Software"/>
<DBFFieldSpec FieldName="SW_VERSION" Form="attribute" Name="SoftwareVersion" FieldType="" Description="Software Version Identifier"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.LIN">
<DBFFileTranslation ID="ID2" ElementSetName="PartsList" ElementName="Part" Description="Parts List Table">
<DBFFieldSpec FieldName="OEM_PARTNO" Form="attribute" Name="TDPartNum" FieldType="" Description="Part Number"/>
<DBFFieldSpec FieldName="ALT_PARTNO" Form="attribute" Name="AltPartNum" FieldType="" Description="Alternate Part Number"/>
<DBFFieldSpec FieldName="ACT_PRICE" Form="attribute" Name="ActPrice" FieldType="" Description="Actual Part Price"/>
<DBFFieldSpec FieldName="LINE_DESC" Form="attribute" Name="TDPartDesc" FieldType="" Description="Part Description"/>
<DBFFieldSpec FieldName="DB_PRICE" Form="attribute" Name="TDEstimate" FieldType="" Description="DB_Price"/>
<DBFFieldSpec FieldName="PART_QTY" Form="attribute" Name="TDPartQty" FieldType="" Description="Part Quantity"/>
<DBFFieldSpec FieldName="LINE_NO" Form="attribute" Name="LineNumber" FieldType="" Description=""/>
<DBFFieldSpec FieldName="UNQ_SEQ" Form="attribute" Name="SequenceNumber" FieldType="" Description=""/>
<DBFFieldSpec FieldName="PART_TYPE" Form="attribute" Name="PartType" FieldType="" Description="Part Type"/>
<DBFFieldSpec FieldName="MOD_LBR_TY" Form="attribute" Name="LaborType" FieldType="" Description="Labor Type"/>
<DBFFieldSpec FieldName="MOD_LB_HRS" Form="attribute" Name="LaborHours" FieldType="" Description="Labor Hours"/>
<DBFFieldSpec FieldName="LBR_OP" Form="attribute" Name="LaborOp" FieldType="" Description="Labor Op"/>
<DBFFieldSpec FieldName="LINE_IND" Form="attribute" Name="SupplementLevel" FieldType="" Description="Supplement Level"/>
<DBFFieldSpec FieldName="TRAN_CODE" Form="attribute" Name="TransactionCode" FieldType="" Description="Transaction Code"/>
<DBFFieldSpec FieldName="GLASS_FLAG" Form="attribute" Name="GlassFlag" FieldType="" Description="Glass Flag"/>
<DBFFieldSpec FieldName="PRT_DSMK_M" Form="attribute" Name="MarkUp" FieldType="" Description="Price Mark up for non OEM parts"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.VEH">
<DBFFileTranslation ID="ID3" ElementName="Vehicle" Description="Vehicle Table">
<DBFFieldSpec FieldName="V_VIN" Form="attribute" Name="TransVIN" FieldType="" Description=""/>
<DBFFieldSpec FieldName="V_MODEL_YR" Form="attribute" Name="TransYear" FieldType="" Description="V_MODEL_YR"/>
<DBFFieldSpec FieldName="V_MAKEDESC" Form="attribute" Name="ManufName" FieldType="" Description="V_MAKEDESC"/>
<DBFFieldSpec FieldName="V_MAKECODE" Form="attribute" Name="ManufCode" FieldType="" Description="V_MAKECODE"/>
<DBFFieldSpec FieldName="V_MODEL" Form="attribute" Name="TransModel" FieldType="" Description="V_MODEL"/>
<DBFFieldSpec FieldName="V_MILEAGE" Form="attribute" Name="TransMileage" FieldType="" Description="V_MILEAGE"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.AD1">
<DBFFileTranslation ID="ID4" ElementName="Admin" Description="Administrative information">
<DBFFieldSpec FieldName="OWNR_LN" Form="attribute" Name="OwnerL" FieldType="" Description="Owner Lastname"/>
<DBFFieldSpec FieldName="OWNR_FN" Form="attribute" Name="OwnerF" FieldType="" Description="Owner Firstname"/>
<DBFFieldSpec FieldName="OWNR_CO_NM" Form="attribute" Name="OwnerCompanyName" FieldType="" Description="Owner Company Name"/>
<DBFFieldSpec FieldName="OWNR_TITLE" Form="attribute" Name="OwnerTitle" FieldType="" Description="Owner Title"/>
<DBFFieldSpec FieldName="OWNR_ADDR1" Form="attribute" Name="OwnerAddr1" FieldType="" Description="Owner Address Line 1"/>
<DBFFieldSpec FieldName="OWNR_ADDR2" Form="attribute" Name="OwnerAddr2" FieldType="" Description="Owner Address Line 2"/>
<DBFFieldSpec FieldName="OWNR_CITY" Form="attribute" Name="OwnerCity" FieldType="" Description="Owner City"/>
<DBFFieldSpec FieldName="OWNR_ST" Form="attribute" Name="OwnerState" FieldType="" Description="Owner State"/>
<DBFFieldSpec FieldName="OWNR_ZIP" Form="attribute" Name="OwnerZip" FieldType="" Description="Owner Zip"/>
<DBFFieldSpec FieldName="OWNR_CTRY" Form="attribute" Name="OwnerCountry" FieldType="" Description="Owner Country"/>
<DBFFieldSpec FieldName="OWNR_PH1" Form="attribute" Name="OwnerPhone1" FieldType="" Description="Owner Primary Phone"/>
<DBFFieldSpec FieldName="OWNR_PH1X" Form="attribute" Name="OwnerPhone1Ext" FieldType="" Description="Owner Primary Phone Extension"/>
<DBFFieldSpec FieldName="OWNR_PH2" Form="attribute" Name="OwnerPhone2" FieldType="" Description="Owner Secondary Phone"/>
<DBFFieldSpec FieldName="OWNR_PH2X" Form="attribute" Name="OwnerPhone2Ext" FieldType="" Description="Owner Secondary Phone Extension"/>
<DBFFieldSpec FieldName="OWNR_FAX" Form="attribute" Name="OwnerFax" FieldType="" Description="Owner Fax"/>
<DBFFieldSpec FieldName="OWNR_FAXX" Form="attribute" Name="OwnerFaxExt" FieldType="" Description="Owner Fax Extension"/>
<DBFFieldSpec FieldName="OWNR_EA" Form="attribute" Name="OwnerEmail" FieldType="" Description="Owner Email Address"/>
<DBFFieldSpec FieldName="INS_CO_NM" Form="attribute" Name="InsuranceCompanyName" FieldType="" Description="Insurance Company Name"/>
<DBFFieldSpec FieldName="INS_CO_ID" Form="attribute" Name="InsuranceCompanyID" FieldType="" Description="Insurance Company Identifier"/>
<DBFFieldSpec FieldName="INS_ADDR1" Form="attribute" Name="InsuranceAddr1" FieldType="" Description="Insurance Address Line 1"/>
<DBFFieldSpec FieldName="INS_ADDR2" Form="attribute" Name="InsuranceAddr2" FieldType="" Description="Insurance Address Line 2"/>
<DBFFieldSpec FieldName="INS_CITY" Form="attribute" Name="InsuranceCity" FieldType="" Description="Insurance City"/>
<DBFFieldSpec FieldName="INS_ST" Form="attribute" Name="InsuranceState" FieldType="" Description="Insurance State"/>
<DBFFieldSpec FieldName="INS_ZIP" Form="attribute" Name="InsuranceZip" FieldType="" Description="Insurance Zip"/>
<DBFFieldSpec FieldName="INS_CTRY" Form="attribute" Name="InsuranceCountry" FieldType="" Description="Insurance Country"/>
<DBFFieldSpec FieldName="INS_PH1" Form="attribute" Name="InsurancePhone1" FieldType="" Description="Insurance Primary Phone"/>
<DBFFieldSpec FieldName="INS_PH1X" Form="attribute" Name="InsurancePhone1Ext" FieldType="" Description="Insurance Primary Phone Extension"/>
<DBFFieldSpec FieldName="INS_PH2" Form="attribute" Name="InsurancePhone2" FieldType="" Description="Insurance Secondary Phone"/>
<DBFFieldSpec FieldName="INS_PH2X" Form="attribute" Name="InsurancePhone2Ext" FieldType="" Description="Insurance Secondary Phone Extension"/>
<DBFFieldSpec FieldName="INS_FAX" Form="attribute" Name="InsuranceFax" FieldType="" Description="Insurance Fax"/>
<DBFFieldSpec FieldName="INS_FAXX" Form="attribute" Name="InsuranceFaxExt" FieldType="" Description="Insurance Fax Extension"/>
<DBFFieldSpec FieldName="INS_EA" Form="attribute" Name="InsuranceEmail" FieldType="" Description="Insurance Email Address"/>
<DBFFieldSpec FieldName="INSD_LN" Form="attribute" Name="InsuranceLName" FieldType="" Description="Insurance Last Name"/>
<DBFFieldSpec FieldName="CLM_NO" Form="attribute" Name="ClaimNumber" FieldType="" Description="Claim Number"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.AD2">
<DBFFileTranslation ID="ID5" ElementName="Admin2" Description="Administrative information2">
<DBFFieldSpec FieldName="EST_CT_LN" Form="attribute" Name="EstimatorL" FieldType="" Description="Estimator Lastname"/>
<DBFFieldSpec FieldName="EST_CT_FN" Form="attribute" Name="EstimatorF" FieldType="" Description="Estimator Firstname"/>
<DBFFieldSpec FieldName="EST_CO_NM" Form="attribute" Name="EstimatorCompanyName" FieldType="" Description="Estimator Company Name"/>
<DBFFieldSpec FieldName="EST_CO_ID" Form="attribute" Name="EstimatorCompanyID" FieldType="" Description="Estimator Company Identifier"/>
<DBFFieldSpec FieldName="EST_ADDR1" Form="attribute" Name="EstimatorAddr1" FieldType="" Description="Estimator Address1"/>
<DBFFieldSpec FieldName="EST_ADDR2" Form="attribute" Name="EstimatorAddr2" FieldType="" Description="Estimator Address2"/>
<DBFFieldSpec FieldName="EST_CITY" Form="attribute" Name="EstimatorCity" FieldType="" Description="Estimator City"/>
<DBFFieldSpec FieldName="EST_ST" Form="attribute" Name="EstimatorState" FieldType="" Description="Estimator State"/>
<DBFFieldSpec FieldName="EST_ZIP" Form="attribute" Name="EstimatorZip" FieldType="" Description="Estimator Zip"/>
<DBFFieldSpec FieldName="EST_CTRY" Form="attribute" Name="EstimatorCountry" FieldType="" Description="Estimator Country"/>
<DBFFieldSpec FieldName="EST_PH1" Form="attribute" Name="EstimatorPhone1" FieldType="" Description="Estimator Primary Phone"/>
<DBFFieldSpec FieldName="EST_PH1X" Form="attribute" Name="EstimatorPhone1Ext" FieldType="" Description="Estimator Primary Phone Extension"/>
<DBFFieldSpec FieldName="EST_PH2" Form="attribute" Name="EstimatorPhone2" FieldType="" Description="Estimator Secondary Phone"/>
<DBFFieldSpec FieldName="EST_PH2X" Form="attribute" Name="EstimatorPhone2Ext" FieldType="" Description="Estimator Secondary Phone Extension"/>
<DBFFieldSpec FieldName="EST_FAX" Form="attribute" Name="EstimatorFax" FieldType="" Description="Estimator Fax"/>
<DBFFieldSpec FieldName="EST_FAXX" Form="attribute" Name="EstimatorFaxExt" FieldType="" Description="Estimator Fax Extension"/>
<DBFFieldSpec FieldName="EST_EA" Form="attribute" Name="EstimatorEmail" FieldType="" Description="Estimator Email Address"/>
<DBFFieldSpec FieldName="EST_LIC_NO" Form="attribute" Name="EstimatorLicenseNumber" FieldType="" Description="Estimator License Number"/>
<DBFFieldSpec FieldName="EST_FILENO" Form="attribute" Name="EstimatorFileNumber" FieldType="" Description="Estimator File Number"/>
<DBFFieldSpec FieldName="RO_IN_DATE" Form="attribute" Name="VehicleInDate" FieldType="" Description="Date arrived in shop"/>
<DBFFieldSpec FieldName="RO_IN_TIME" Form="attribute" Name="VehicleInTime" FieldType="" Description="Time arrived in shop"/>
<DBFFieldSpec FieldName="TAR_DATE" Form="attribute" Name="TargetVehicleOutDate" FieldType="" Description="Target date to be completed"/>
<DBFFieldSpec FieldName="TAR_TIME" Form="attribute" Name="TargetVehicleOutTime" FieldType="" Description="Target time to be completed"/>
<DBFFieldSpec FieldName="RO_CMPDATE" Form="attribute" Name="VehicleOutDate" FieldType="" Description="Date completed"/>
<DBFFieldSpec FieldName="RO_CMPTIME" Form="attribute" Name="VehicleOutTime" FieldType="" Description="Time completed"/>
<DBFFieldSpec FieldName="RF_CO_NM" Form="attribute" Name="BodyShopName" FieldType="" Description="Body Shop Name"/>
</DBFFileTranslation>
</FileTranslation>
<FileTranslation TranslatorID="OECImportEngine.DBFTranslator" FileNameMask="*.TTL">
<DBFFileTranslation ID="ID6" ElementName="Total" Description="Total Table">
<DBFFieldSpec FieldName="G_TTL_AMT" Form="attribute" Name="GrandTotalAmount" FieldType="" Description=""/>
</DBFFileTranslation>
</FileTranslation>
</FileTranslations>
<StyleSheetTranslation>
<StyleSheetURL>Mitchell.xsl</StyleSheetURL>
</StyleSheetTranslation>
</TranslationDescriptor>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -22,7 +22,7 @@ hooks.js:
module.exports = [
{
path: "/pull",
command: "git pull && yarn && pm2 restart 0",
command: "git pull && npm i",
cwd: "/home/ubuntu/io/",
method: "post",
},

View File

@@ -28,7 +28,7 @@ import JobsShow from "../jobs/jobs.show";
const httpLink = new HttpLink({
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
headers: {
"x-hasura-admin-secret": `Dev-BodyShopApp!`,
"x-hasura-admin-secret": `Dev-BodyShopAppBySnaptSoftware!`,
// 'Authorization': `Bearer xxxx`,
},
});
@@ -67,7 +67,7 @@ const client = new ApolloClient({
// uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
// cache: new InMemoryCache(),
// headers: {
// "x-hasura-admin-secret": `Dev-BodyShopApp!`,
// "x-hasura-admin-secret": `Dev-BodyShopAppBySnaptSoftware!`,
// // 'Authorization': `Bearer xxxx`,
// },
// });

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +1,9 @@
// craco.config.js
const TerserPlugin = require("terser-webpack-plugin");
const CracoLessPlugin = require("craco-less");
const SentryWebpackPlugin = require("@sentry/webpack-plugin");
module.exports = {
plugins: [
{
plugin: SentryWebpackPlugin,
options: {
// sentry-cli configuration
authToken:
"6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260",
org: "snapt-software",
project: "imexonline",
release: process.env.REACT_APP_GIT_SHA,
// webpack-specific configuration
include: ".",
ignore: ["node_modules", "webpack.config.js"],
},
},
{
plugin: CracoLessPlugin,
options: {
@@ -28,9 +12,7 @@ module.exports = {
modifyVars: {
...(process.env.NODE_ENV === "development"
? { "@primary-color": "#a51d1d" }
: {
//"@primary-color": "#1DA57A"
}),
: { "@primary-color": "#1DA57A" }),
// "@primary-color": " #1890ff", // primary color for all components
// "@link-color": "#1890ff", // link color
// "@success-color": "#52c41a", // success state color
@@ -69,5 +51,4 @@ module.exports = {
},
}),
},
devtool: "source-map",
};

13
client/madge-graph.svg Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.44.1 (20200629.0846)
-->
<!-- Title: G Pages: 1 -->
<svg width="43pt" height="43pt"
viewBox="0.00 0.00 43.20 43.20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(21.6 21.6)">
<title>G</title>
<polygon fill="#111111" stroke="transparent" points="-21.6,21.6 -21.6,-21.6 21.6,-21.6 21.6,21.6 -21.6,21.6"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 613 B

42958
client/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,95 +2,80 @@
"name": "bodyshop",
"version": "0.1.1",
"private": true,
"proxy": "http://localhost:4000",
"proxy": "http://localhost:5000",
"dependencies": {
"@apollo/client": "^3.4.17",
"@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^6.4.0",
"@fingerprintjs/fingerprintjs": "^3.3.0",
"@openreplay/tracker": "^3.4.7",
"@openreplay/tracker-assist": "^3.4.4",
"@openreplay/tracker-graphql": "^3.0.0",
"@openreplay/tracker-redux": "^3.0.0",
"@sentry/react": "^6.14.3",
"@sentry/tracing": "^6.14.3",
"@splitsoftware/splitio-react": "^1.3.0",
"@stripe/react-stripe-js": "^1.6.0",
"@stripe/stripe-js": "^1.21.1",
"@tanem/react-nprogress": "^3.0.82",
"antd": "^4.16.13",
"@apollo/client": "^3.3.17",
"@craco/craco": "^6.1.2",
"@fingerprintjs/fingerprintjs": "^3.1.2",
"@lourenci/react-kanban": "^2.1.0",
"@sentry/react": "^6.3.6",
"@sentry/tracing": "^6.3.6",
"@stripe/react-stripe-js": "^1.4.0",
"@stripe/stripe-js": "^1.14.0",
"@tanem/react-nprogress": "^3.0.65",
"antd": "^4.15.5",
"apollo-link-logger": "^2.0.0",
"axios": "^0.24.0",
"craco-less": "^1.20.0",
"dinero.js": "^1.9.1",
"dotenv": "^10.0.0",
"enquire-js": "^0.2.1",
"axios": "^0.21.1",
"craco-less": "^1.17.1",
"dinero.js": "^1.8.1",
"dotenv": "^9.0.2",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"firebase": "^9.4.1",
"graphql": "^16.0.1",
"i18next": "^21.4.2",
"i18next-browser-languagedetector": "^6.1.2",
"jsoneditor": "^9.5.7",
"firebase": "^8.6.0",
"graphql": "^15.5.0",
"i18next": "^20.2.2",
"i18next-browser-languagedetector": "^6.1.1",
"jsoneditor": "^9.4.1",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.42",
"logrocket": "^2.1.1",
"markerjs2": "^2.17.0",
"libphonenumber-js": "^1.9.17",
"logrocket": "^1.2.0",
"moment-business-days": "^1.2.0",
"phone": "^3.1.9",
"phone": "^2.4.21",
"preval.macro": "^5.0.0",
"prop-types": "^15.7.2",
"query-string": "^7.0.1",
"rc-queue-anim": "^2.0.0",
"rc-scroll-anim": "^2.7.6",
"query-string": "^7.0.0",
"react": "^17.0.1",
"react-big-calendar": "^0.38.1",
"react-big-calendar": "^0.33.2",
"react-color": "^2.19.3",
"react-cookie": "^4.1.1",
"react-dom": "^17.0.1",
"react-drag-listview": "^0.1.8",
"react-grid-gallery": "^0.5.5",
"react-grid-layout": "^1.3.0",
"react-i18next": "^11.14.2",
"react-icons": "^4.3.1",
"react-number-format": "^4.8.0",
"react-redux": "^7.2.6",
"react-resizable": "^3.0.4",
"react-router-dom": "^5.3.0",
"react-i18next": "^11.8.15",
"react-icons": "^4.2.0",
"react-number-format": "^4.5.5",
"react-redux": "^7.2.4",
"react-resizable": "^3.0.1",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3",
"recharts": "^2.1.6",
"redux": "^4.1.2",
"recharts": "^2.0.7",
"redux": "^4.1.0",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"redux-state-sync": "^3.1.2",
"reselect": "^4.1.2",
"sass": "^1.43.4",
"socket.io-client": "^4.3.2",
"styled-components": "^5.3.3",
"subscriptions-transport-ws": "^0.11.0",
"web-vitals": "^2.1.2",
"workbox-background-sync": "^6.3.0",
"workbox-broadcast-update": "^6.3.0",
"workbox-cacheable-response": "^6.3.0",
"workbox-core": "^6.3.0",
"workbox-expiration": "^6.3.0",
"workbox-google-analytics": "^6.3.0",
"workbox-navigation-preload": "^6.3.0",
"workbox-precaching": "^6.3.0",
"workbox-range-requests": "^6.3.0",
"workbox-routing": "^6.3.0",
"workbox-strategies": "^6.3.0",
"workbox-streams": "^6.3.0"
"reselect": "^4.0.0",
"sass": "^1.32.13",
"styled-components": "^5.3.0",
"subscriptions-transport-ws": "^0.9.18",
"web-vitals": "^1.1.2",
"workbox-background-sync": "^6.1.5",
"workbox-broadcast-update": "^6.1.5",
"workbox-cacheable-response": "^6.1.5",
"workbox-core": "^6.1.5",
"workbox-expiration": "^6.1.5",
"workbox-google-analytics": "^6.1.5",
"workbox-navigation-preload": "^6.1.5",
"workbox-precaching": "^6.1.5",
"workbox-range-requests": "^6.1.5",
"workbox-routing": "^6.1.5",
"workbox-strategies": "^6.1.5",
"workbox-streams": "^6.1.5"
},
"scripts": {
"postinstall": "patch-package",
"analyze": "source-map-explorer 'build/static/js/*.js'",
"start": "craco start",
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
"build:test": "env-cmd -f .env.test yarn run build",
"build-deploy:test": "yarn run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
"build:test": "env-cmd -f .env.test npm run build",
"build-deploy:test": "npm run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
"buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` react-scripts build",
"test": "craco test",
"eject": "react-scripts eject",
@@ -115,8 +100,6 @@
]
},
"devDependencies": {
"@sentry/webpack-plugin": "^1.18.3",
"patch-package": "^6.4.7",
"redux-logger": "^3.0.6",
"source-map-explorer": "^2.5.2"
}

File diff suppressed because one or more lines are too long

View File

@@ -1,56 +1,53 @@
// Scripts for firebase and firebase messaging
importScripts("https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/8.2.0/firebase-messaging.js");
importScripts("https://www.gstatic.com/firebasejs/7.14.2/firebase-app.js");
importScripts(
"https://www.gstatic.com/firebasejs/7.14.2/firebase-messaging.js"
);
// Initialize the Firebase app in the service worker by passing the generated config
let firebaseConfig;
switch (this.location.hostname) {
case "localhost":
firebaseConfig = {
apiKey: "AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc",
authDomain: "imex-dev.firebaseapp.com",
databaseURL: "https://imex-dev.firebaseio.com",
projectId: "imex-dev",
storageBucket: "imex-dev.appspot.com",
messagingSenderId: "759548147434",
appId: "1:759548147434:web:e8239868a48ceb36700993",
measurementId: "G-K5XRBVVB4S",
};
break;
case "test.imex.online":
firebaseConfig = {
apiKey: "AIzaSyBw7_GTy7GtQyfkIRPVrWHEGKfcqeyXw0c",
authDomain: "imex-test.firebaseapp.com",
projectId: "imex-test",
storageBucket: "imex-test.appspot.com",
messagingSenderId: "991923618608",
appId: "1:991923618608:web:633437569cdad78299bef5",
// measurementId: "${config.measurementId}",
};
break;
case "imex.online":
default:
firebaseConfig = {
apiKey: "AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU",
authDomain: "imex-prod.firebaseapp.com",
databaseURL: "https://imex-prod.firebaseio.com",
projectId: "imex-prod",
storageBucket: "imex-prod.appspot.com",
messagingSenderId: "253497221485",
appId: "1:253497221485:web:3c81c483b94db84b227a64",
measurementId: "G-NTWBKG2L0M",
};
}
firebase.initializeApp({
apiKey: "AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU",
authDomain: "imex-prod.firebaseapp.com",
databaseURL: "https://imex-prod.firebaseio.com",
projectId: "imex-prod",
storageBucket: "imex-prod.appspot.com",
messagingSenderId: "253497221485",
appId: "1:253497221485:web:3c81c483b94db84b227a64",
measurementId: "G-NTWBKG2L0M",
});
firebase.initializeApp(firebaseConfig);
// firebase.initializeApp({
// apiKey: "AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc",
// authDomain: "imex-dev.firebaseapp.com",
// databaseURL: "https://imex-dev.firebaseio.com",
// projectId: "imex-dev",
// storageBucket: "imex-dev.appspot.com",
// messagingSenderId: "759548147434",
// appId: "1:759548147434:web:e8239868a48ceb36700993",
// measurementId: "G-K5XRBVVB4S",
// });
// Retrieve firebase messaging
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) {
// Customize notification here
const channel = new BroadcastChannel("imex-sw-messages");
channel.postMessage(payload);
//self.registration.showNotification(notificationTitle, notificationOptions);
self.addEventListener("fetch", (fetch) => {
//required for installation as a PWA. Can ignore for now.
//console.log("fetch", fetch);
});
messaging.setBackgroundMessageHandler(function (payload) {
return self.registration.showNotification(
"[SW]" + payload.notification.title,
payload.notification
);
});
//Handles the notification getting clicked.
self.addEventListener("notificationclick", function (event) {
console.log("SW notificationclick", event);
// event.notification.close();
if (event.action === "archive") {
// Archive action was clicked
archiveEmail();
} else {
// Main body of notification was clicked
clients.openWindow("/inbox");
}
});

View File

@@ -8,61 +8,13 @@
<meta name="description" content="ImEX Online" />
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
<link rel="apple-touch-icon" href="logo192.png" />
<script type="text/javascript">
window.$crisp = [];
window.CRISP_WEBSITE_ID = "36724f62-2eb0-4b29-9cdd-9905fb99913e";
(function () {
d = document;
s = d.createElement("script");
s.src = "https://client.crisp.chat/l.js";
s.async = 1;
d.getElementsByTagName("head")[0].appendChild(s);
})();
</script>
<script>
!(function () {
"use strict";
var e = [
"debug",
"destroy",
"do",
"help",
"identify",
"is",
"off",
"on",
"ready",
"render",
"reset",
"safe",
"set",
];
if (window.noticeable)
console.warn("Noticeable SDK code snippet loaded more than once");
else {
var n = (window.noticeable = window.noticeable || []);
function t(e) {
return function () {
var t = Array.prototype.slice.call(arguments);
return t.unshift(e), n.push(t), n;
};
}
!(function () {
for (var o = 0; o < e.length; o++) {
var r = e[o];
n[r] = t(r);
}
})(),
(function () {
var e = document.createElement("script");
(e.async = !0), (e.src = "https://sdk.noticeable.io/l.js");
var n = document.head;
n.insertBefore(e, n.firstChild);
})();
}
})();
</script>
<!-- <script
data-jsd-embedded
data-key="51adb36e-ee16-46b1-a4c6-4b6d5fcd8530"
data-base-url="https://jsd-widget.atlassian.com"
src="https://jsd-widget.atlassian.com/assets/embed.js"
></script> -->
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/

View File

@@ -1,32 +1,19 @@
import { ApolloProvider } from "@apollo/client";
import { SplitFactory, SplitSdk } from "@splitsoftware/splitio-react";
import { ConfigProvider } from "antd";
import enLocale from "antd/es/locale/en_US";
import LogRocket from "logrocket";
import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
import client from "../utils/GraphQLClient";
import App from "./App";
import { useTranslation } from "react-i18next";
moment.locale("en-US");
//tracker.start();
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
export const factory = SplitSdk({
core: {
authorizationKey: process.env.REACT_APP_SPLIT_API,
key: "anon",
},
});
export default function AppContainer() {
const { t } = useTranslation();
return (
<ApolloProvider client={client}>
<ConfigProvider
@@ -41,9 +28,7 @@ export default function AppContainer() {
}}
>
<GlobalLoadingBar />
<SplitFactory factory={factory}>
<App />
</SplitFactory>
<App />
</ConfigProvider>
</ApolloProvider>
);

View File

@@ -1,23 +1,19 @@
import { Button, Result } from "antd";
import React, { lazy, Suspense, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Route, Switch } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import DocumentEditorContainer from "../components/document-editor/document-editor.container";
import ErrorBoundary from "../components/error-boundary/error-boundary.component";
//Component Imports
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
import DisclaimerPage from "../pages/disclaimer/disclaimer.page";
import AboutPage from "../pages/about/about.page";
import TechPageContainer from "../pages/tech/tech.page.container";
import { setOnline } from "../redux/application/application.actions";
import { selectOnline } from "../redux/application/application.selectors";
import { checkUserSession } from "../redux/user/user.actions";
import { selectCurrentUser } from "../redux/user/user.selectors";
import PrivateRoute from "../utils/private-route";
import "./App.styles.scss";
import LandingPage from "../pages/landing/landing.page";
const LandingPage = lazy(() => import("../pages/landing/landing.page"));
const ResetPassword = lazy(() =>
import("../pages/reset-password/reset-password.component")
);
@@ -31,58 +27,25 @@ const MobilePaymentContainer = lazy(() =>
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
online: selectOnline,
});
const mapDispatchToProps = (dispatch) => ({
checkUserSession: () => dispatch(checkUserSession()),
setOnline: (isOnline) => dispatch(setOnline(isOnline)),
});
export function App({ checkUserSession, currentUser, online, setOnline }) {
export function App({ checkUserSession, currentUser }) {
useEffect(() => {
if (!navigator.onLine) {
setOnline(false);
}
checkUserSession();
}, [checkUserSession, setOnline]);
}, [checkUserSession]);
//const b = Grid.useBreakpoint();
// console.log("Breakpoints:", b);
const { t } = useTranslation();
window.addEventListener("offline", function (e) {
setOnline(false);
});
window.addEventListener("online", function (e) {
setOnline(true);
});
if (currentUser.authorized === null) {
return <LoadingSpinner message={t("general.labels.loggingin")} />;
}
if (!online)
return (
<Result
status="warning"
title={t("general.labels.nointernet")}
subTitle={t("general.labels.nointernet_sub")}
extra={
<Button
type="primary"
onClick={() => {
window.location.reload();
}}
>
{t("general.actions.refresh")}
</Button>
}
/>
);
return (
<Switch>
<Suspense fallback={<LoadingSpinner message="ImEX Online" />}>
@@ -99,7 +62,7 @@ export function App({ checkUserSession, currentUser, online, setOnline }) {
<Route exact path="/csi/:surveyId" component={CsiPage} />
</ErrorBoundary>
<ErrorBoundary>
<Route exact path="/disclaimer" component={DisclaimerPage} />
<Route exact path="/about" component={AboutPage} />
</ErrorBoundary>
<ErrorBoundary>
<Route
@@ -122,13 +85,6 @@ export function App({ checkUserSession, currentUser, online, setOnline }) {
component={TechPageContainer}
/>
</ErrorBoundary>
<ErrorBoundary>
<PrivateRoute
isAuthorized={currentUser.authorized}
path="/edit"
component={DocumentEditorContainer}
/>
</ErrorBoundary>
</Suspense>
</Switch>
);

View File

@@ -118,19 +118,3 @@
.production-list-min-height {
min-height: 19px;
}
#noticeable-widget {
iframe {
z-index: 2 !important;
}
}
.react-kanban-column {
background-color: #ddd !important;
}
.production-list-table {
td.ant-table-column-sort {
background: unset;
}
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 51 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" ?><svg style="enable-background:new 0 0 128 128;" version="1.1" viewBox="0 0 128 128" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style type="text/css">
.st0{fill:none;stroke:#000000;stroke-width:8;stroke-miterlimit:10;}
.st1{display:none;}
.st2{display:inline;opacity:0.25;fill:#F45EFD;}
</style><g id="_x31_2_3D_Printing"/><g id="_x31_1_VR_Gear"/><g id="_x31_0_Virtual_reality"/><g id="_x39__Augmented_reality"/><g id="_x38__Teleport"/><g id="_x37__Glassess"/><g id="_x36__Folding_phone"/><g id="_x35__Drone"/><g id="_x34__Retina_scan"/><g id="_x33__Smartwatch"/><g id="_x32__Bionic_Arm"/><g id="_x31__Chip"><g><path d="M108,40c-5.2,0-9.6,3.3-11.3,8H84V32h-8V20h-8v12h-8V20h-8v12h-8v16H24v-8.7c4.7-1.7,8-6.1,8-11.3c0-6.6-5.4-12-12-12 S8,21.4,8,28c0,5.2,3.3,9.6,8,11.3V56h28v8H16v16.7c-4.7,1.7-8,6.1-8,11.3c0,6.6,5.4,12,12,12s12-5.4,12-12c0-5.2-3.3-9.6-8-11.3 V72h20v16h8v12h8V88h8v12h8V88h8V72h8v16.7c-4.7,1.7-8,6.1-8,11.3c0,6.6,5.4,12,12,12s12-5.4,12-12c0-5.2-3.3-9.6-8-11.3V64H84v-8 h12.7c1.7,4.7,6.1,8,11.3,8c6.6,0,12-5.4,12-12S114.6,40,108,40z M20,32c-2.2,0-4-1.8-4-4s1.8-4,4-4s4,1.8,4,4S22.2,32,20,32z M20,96c-2.2,0-4-1.8-4-4s1.8-4,4-4s4,1.8,4,4S22.2,96,20,96z M76,80H52V40h24V80z M96,96c2.2,0,4,1.8,4,4s-1.8,4-4,4s-4-1.8-4-4 S93.8,96,96,96z M108,56c-2.2,0-4-1.8-4-4s1.8-4,4-4s4,1.8,4,4S110.2,56,108,56z"/><rect height="8" width="8" x="56" y="64"/></g></g><g class="st1" id="Guide"><path class="st2" d="M120,8v112H8V8H120 M128,0H0v128h128V0L128,0z"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.6 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.4 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.6 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -1,9 +1,25 @@
import Axios from "axios";
import React from "react";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
export default function Test() {
const handleQbSignIn = async () => {
const result = await Axios.post("/qbo/authorize", { userId: "1234" });
console.log("handleQbSignIn -> result", result.data);
// window.open(result.data, "_blank", "toolbar=0,location=0,menubar=0");
var parameters = "location=1,width=800,height=650";
parameters +=
",left=" +
(window.screen.width - 800) / 2 +
",top=" +
(window.screen.height - 650) / 2;
// Launch Popup
window.open(result.data, "connectPopup", parameters);
};
return (
<div>
<QboAuthorizeComponent />
<button onClick={handleQbSignIn}>Sign Into Qb.</button>
</div>
);
}

View File

@@ -9,25 +9,8 @@ import PayableExportAll from "../payable-export-all-button/payable-export-all-bu
import { DateFormatter } from "../../utils/DateFormatter";
import queryString from "query-string";
import { logImEXEvent } from "../../firebase/firebase.utils";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(AccountingPayablesTableComponent);
export function AccountingPayablesTableComponent({ bodyshop, loading, bills }) {
export default function AccountingPayablesTableComponent({ loading, bills }) {
const { t } = useTranslation();
const [selectedBills, setSelectedBills] = useState([]);
const [transInProgress, setTransInProgress] = useState(false);
@@ -126,17 +109,6 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills }) {
<Checkbox disabled checked={record.is_credit_memo} />
),
},
{
title: t("exportlogs.labels.attempts"),
dataIndex: "attempts",
key: "attempts",
render: (text, record) => {
const success = record.exportlogs.filter((e) => e.successful).length;
const attempts = record.exportlogs.length;
return `${success}/${attempts}`;
},
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
@@ -149,7 +121,6 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills }) {
billId={record.id}
disabled={transInProgress || !!record.exported}
loadingCallback={setTransInProgress}
setSelectedBills={setSelectedBills}
/>
</div>
),
@@ -183,9 +154,6 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills }) {
loadingCallback={setTransInProgress}
completedCallback={setSelectedBills}
/>
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent />
)}
<Input
value={state.search}
onChange={handleSearch}

View File

@@ -5,29 +5,11 @@ import { Link } from "react-router-dom";
import { logImEXEvent } from "../../firebase/firebase.utils";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters";
import { alphaSort } from "../../utils/sorters";
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { PaymentsExportAllButton } from "../payments-export-all-button/payments-export-all-button.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(AccountingPayablesTableComponent);
export function AccountingPayablesTableComponent({
bodyshop,
export default function AccountingPayablesTableComponent({
loading,
payments,
}) {
@@ -59,12 +41,19 @@ export function AccountingPayablesTableComponent({
title: t("payments.fields.date"),
dataIndex: "date",
key: "date",
sorter: (a, b) => dateSort(a.date, b.date),
sorter: (a, b) => alphaSort(a.date, b.date),
sortOrder:
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
},
{
title: t("payments.fields.date"),
dataIndex: "date",
key: "date",
sorter: (a, b) => alphaSort(a.date, b.date),
sortOrder:
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
@@ -72,7 +61,7 @@ export function AccountingPayablesTableComponent({
ellipsis: true,
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
sortOrder:
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
state.sortedInfo.columnKey === "ownr_ln" && state.sortedInfo.order,
render: (text, record) => {
return record.job.owner ? (
<Link to={"/manage/owners/" + record.job.owner.id}>
@@ -126,17 +115,7 @@ export function AccountingPayablesTableComponent({
<DateTimeFormatter>{record.exportedat}</DateTimeFormatter>
),
},
{
title: t("exportlogs.labels.attempts"),
dataIndex: "attempts",
key: "attempts",
render: (text, record) => {
const success = record.exportlogs.filter((e) => e.successful).length;
const attempts = record.exportlogs.length;
return `${success}/${attempts}`;
},
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
@@ -148,7 +127,6 @@ export function AccountingPayablesTableComponent({
paymentId={record.id}
disabled={transInProgress || !!record.exportedat}
loadingCallback={setTransInProgress}
setSelectedPayments={setSelectedPayments}
/>
),
},
@@ -181,9 +159,6 @@ export function AccountingPayablesTableComponent({
loadingCallback={setTransInProgress}
completedCallback={setSelectedPayments}
/>
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent />
)}
<Input
value={state.search}
onChange={handleSearch}

View File

@@ -8,26 +8,7 @@ import { alphaSort } from "../../utils/sorters";
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(AccountingReceivablesTableComponent);
export function AccountingReceivablesTableComponent({
bodyshop,
loading,
jobs,
}) {
export default function AccountingReceivablesTableComponent({ loading, jobs }) {
const { t } = useTranslation();
const [selectedJobs, setSelectedJobs] = useState([]);
const [transInProgress, setTransInProgress] = useState(false);
@@ -133,28 +114,17 @@ export function AccountingReceivablesTableComponent({
);
},
},
{
title: t("exportlogs.labels.attempts"),
dataIndex: "attempts",
key: "attempts",
render: (text, record) => {
const success = record.exportlogs.filter((e) => e.successful).length;
const attempts = record.exportlogs.length;
return `${success}/${attempts}`;
},
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
key: "actions",
sorter: (a, b) => a.clm_total - b.clm_total,
render: (text, record) => (
<Space wrap>
<JobExportButton
jobId={record.id}
disabled={!!record.date_exported}
setSelectedJobs={setSelectedJobs}
/>
<Link to={`/manage/jobs/${record.id}/close`}>
<Button>{t("jobs.labels.viewallocations")}</Button>
@@ -199,17 +169,12 @@ export function AccountingReceivablesTableComponent({
<Card
extra={
<Space wrap>
{!bodyshop.cdk_dealerid && !bodyshop.pbs_serialnumber && (
<JobsExportAllButton
jobIds={selectedJobs}
disabled={transInProgress || selectedJobs.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedJobs}
/>
)}
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent />
)}
<JobsExportAllButton
jobIds={selectedJobs}
disabled={transInProgress || selectedJobs.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedJobs}
/>
<Input.Search
value={state.search}
onChange={handleSearch}

View File

@@ -26,8 +26,6 @@ import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-bu
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -35,8 +33,6 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({
setPartsOrderContext: (context) =>
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(
@@ -44,10 +40,7 @@ export default connect(
mapDispatchToProps
)(BillDetailEditcontainer);
export function BillDetailEditcontainer({
setPartsOrderContext,
insertAuditTrail,
}) {
export function BillDetailEditcontainer({ setPartsOrderContext }) {
const search = queryString.parse(useLocation().search);
const history = useHistory();
const { t } = useTranslation();
@@ -141,12 +134,6 @@ export function BillDetailEditcontainer({
});
await Promise.all(updates);
insertAuditTrail({
jobid: bill.jobid,
billid: search.billid,
operation: AuditTrailMapping.billupdated(bill.invoice_number),
});
await refetch();
form.setFieldsValue(transformData(data));
form.resetFields();

View File

@@ -11,17 +11,13 @@ import {
QUERY_JOB_LBR_ADJUSTMENTS,
UPDATE_JOB,
} from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import confirmDialog from "../../utils/asyncConfirm";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import BillFormContainer from "../bill-form/bill-form.container";
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
import { handleUpload } from "../documents-upload/documents-upload.utility";
const mapStateToProps = createStructuredSelector({
@@ -31,8 +27,6 @@ const mapStateToProps = createStructuredSelector({
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
function BillEnterModalContainer({
@@ -40,7 +34,6 @@ function BillEnterModalContainer({
toggleModalVisible,
bodyshop,
currentUser,
insertAuditTrail,
}) {
const [form] = Form.useForm();
const { t } = useTranslation();
@@ -50,31 +43,7 @@ function BillEnterModalContainer({
const [loading, setLoading] = useState(false);
const client = useApolloClient();
const formValues = useMemo(() => {
return {
...billEnterModal.context.bill,
jobid:
(billEnterModal.context.job && billEnterModal.context.job.id) || null,
federal_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.federal_tax_rate) ||
0,
state_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.state_tax_rate) ||
0,
local_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.local_tax_rate) ||
0,
};
}, [billEnterModal, bodyshop]);
const handleFinish = async (values) => {
let totals = CalculateBillTotal(values);
if (totals.discrepancy.getAmount() !== 0) {
if (!(await confirmDialog(t("bills.labels.savewithdiscrepancy")))) {
return;
}
}
setLoading(true);
const { upload, location, ...remainingValues } = values;
@@ -112,9 +81,8 @@ function BillEnterModalContainer({
},
],
},
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
});
console.log("adjustmentsToInsert", adjustmentsToInsert);
const adjKeys = Object.keys(adjustmentsToInsert);
if (adjKeys.length > 0) {
//Query the adjustments, merge, and update them.
@@ -147,12 +115,7 @@ function BillEnterModalContainer({
message: JSON.stringify(jobUpdate.errors),
}),
});
return;
}
insertAuditTrail({
jobid: values.jobid,
operation: AuditTrailMapping.jobmodifylbradj(),
});
}
if (!!r1.errors) {
@@ -208,15 +171,9 @@ function BillEnterModalContainer({
});
if (billEnterModal.actions.refetch) billEnterModal.actions.refetch();
insertAuditTrail({
jobid: values.jobid,
billid: billId,
operation: AuditTrailMapping.billposted(remainingValues.invoice_number),
});
if (enterAgain) {
form.resetFields();
form.setFieldsValue({ ...form.getFieldsValue(), billlines: [] });
form.setFieldsValue({ billlines: [] });
} else {
toggleModalVisible();
}
@@ -234,6 +191,23 @@ function BillEnterModalContainer({
if (enterAgain) form.submit();
}, [enterAgain, form]);
const formValues = useMemo(() => {
return {
...billEnterModal.context.bill,
jobid:
(billEnterModal.context.job && billEnterModal.context.job.id) || null,
federal_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.federal_tax_rate) ||
0,
state_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.state_tax_rate) ||
0,
local_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.local_tax_rate) ||
0,
};
}, [billEnterModal, bodyshop]);
useEffect(() => {
if (billEnterModal.visible) {
form.setFieldsValue(formValues);
@@ -251,10 +225,7 @@ function BillEnterModalContainer({
keyboard="false"
onOk={() => form.submit()}
onCancel={handleCancel}
afterClose={() => {
form.resetFields();
setLoading(false);
}}
afterClose={() => form.resetFields()}
footer={
<Space>
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>

View File

@@ -1,8 +1,6 @@
import Icon, { UploadOutlined } from "@ant-design/icons";
import { UploadOutlined } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client";
import { MdOpenInNew } from "react-icons/md";
import {
Alert,
Divider,
Form,
Input,
@@ -15,7 +13,6 @@ import {
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { CHECK_BILL_INVOICE_NUMBER } from "../../graphql/bills.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
@@ -44,7 +41,6 @@ export function BillFormComponent({
loadLines,
billEdit,
disableInvNumber,
job,
}) {
const { t } = useTranslation();
const client = useApolloClient();
@@ -54,10 +50,6 @@ export function BillFormComponent({
setDiscount(opt.discount);
};
useEffect(() => {
if (job) form.validateFields(["is_credit_memo"]);
}, [job, form]);
useEffect(() => {
if (form.getFieldValue("vendorid") && vendorAutoCompleteOptions) {
const vendorId = form.getFieldValue("vendorid");
@@ -97,7 +89,7 @@ export function BillFormComponent({
<JobSearchSelect
disabled={billEdit || disabled}
convertedOnly
notExported={false}
// notExported={false}
onBlur={() => {
if (form.getFieldValue("jobid") !== null) {
loadLines({ variables: { id: form.getFieldValue("jobid") } });
@@ -114,18 +106,6 @@ export function BillFormComponent({
required: true,
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
validator(rule, value) {
if (
value &&
!getFieldValue(["isinhouse"]) &&
value === bodyshop.inhousevendorid
) {
return Promise.reject(t("bills.validation.manualinhouse"));
}
return Promise.resolve();
},
}),
]}
>
<VendorSearchSelect
@@ -135,30 +115,6 @@ export function BillFormComponent({
/>
</Form.Item>
</LayoutFormRow>
{job &&
job.ious &&
job.ious.length > 0 &&
job.ious.map((iou) => (
<Alert
key={iou.id}
type="warning"
message={
<Space>
{t("bills.labels.iouexists")}
<Link
target="_blank"
rel="noopener noreferrer"
to={`/manage/jobs/${iou.id}?tab=repairdata`}
>
<Space>
{iou.ro_number}
<Icon component={MdOpenInNew} />
</Space>
</Link>
</Space>
}
/>
))}
<LayoutFormRow>
<Form.Item
label={t("bills.fields.invoice_number")}
@@ -219,22 +175,6 @@ export function BillFormComponent({
label={t("bills.fields.is_credit_memo")}
name="is_credit_memo"
valuePropName="checked"
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
if (
(job.status === bodyshop.md_ro_statuses.default_invoiced ||
job.status === bodyshop.md_ro_statuses.default_exported ||
job.status === bodyshop.md_ro_statuses.default_void) &&
(value === false || !value)
) {
return Promise.reject(t("bills.labels.onlycmforinvoiced"));
}
return Promise.resolve();
},
}),
]}
>
<Switch />
</Form.Item>
@@ -364,7 +304,6 @@ export function BillFormComponent({
responsibilityCenters={responsibilityCenters}
disabled={disabled}
/>
<Form.Item
name="upload"
label="Upload"

View File

@@ -34,7 +34,6 @@ export function BillFormContainer({
}
loadLines={loadLines}
lineData={lineData ? lineData.joblines : []}
job={lineData ? lineData.jobs_by_pk : null}
responsibilityCenters={bodyshop.md_responsibility_centers || null}
disableInvNumber={disableInvNumber}
/>

View File

@@ -7,14 +7,13 @@ import {
Select,
Space,
Switch,
Table
Table,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CiecaSelect from "../../utils/Ciecaselect";
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
@@ -73,13 +72,9 @@ export function BillEnterModalLinesComponent({
quantity: opt.part_qty || 1,
actual_price: opt.cost,
cost_center: opt.part_type
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
? opt.part_type
: responsibilityCenters.defaults &&
(responsibilityCenters.defaults.costs[
opt.part_type
] ||
null)
? responsibilityCenters.defaults.costs[
opt.part_type
] || null
: null,
};
}
@@ -159,7 +154,18 @@ export function BillEnterModalLinesComponent({
setFieldsValue({
billlines: getFieldsValue("billlines").billlines.map(
(item, idx) => {
console.log("Checking", index, idx);
if (idx === index) {
console.log(
"Found and setting.",
!!item.actual_cost
? item.actual_cost
: Math.round(
(parseFloat(e.target.value) * (1 - discount) +
Number.EPSILON) *
100
) / 100
);
return {
...item,
actual_cost: !!item.actual_cost
@@ -227,7 +233,6 @@ export function BillEnterModalLinesComponent({
key: `${field.index}cost_center`,
name: [field.name, "cost_center"],
label: t("billlines.fields.cost_center"),
valuePropName: "value",
rules: [
{
required: true,
@@ -237,12 +242,10 @@ export function BillEnterModalLinesComponent({
};
},
formInput: (record, index) => (
<Select showSearch style={{ minWidth: "3rem" }} disabled={disabled}>
{bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? CiecaSelect(true, false)
: responsibilityCenters.costs.map((item) => (
<Select.Option key={item.name}>{item.name}</Select.Option>
))}
<Select style={{ minWidth: "3rem" }} disabled={disabled}>
{responsibilityCenters.costs.map((item) => (
<Select.Option key={item.name}>{item.name}</Select.Option>
))}
</Select>
),
},
@@ -497,9 +500,9 @@ const EditableCell = ({
labelCol={{ span: 0 }}
{...(formItemProps && formItemProps(record))}
>
{(formInput && formInput(record, record.name)) || children}
{(formInput && formInput(record, record.key)) || children}
</Form.Item>
{additional && additional(record, record.name)}
{additional && additional(record, record.key)}
</Space>
</td>
);
@@ -511,7 +514,7 @@ const EditableCell = ({
name={dataIndex}
{...(formItemProps && formItemProps(record))}
>
{(formInput && formInput(record, record.name)) || children}
{(formInput && formInput(record, record.key)) || children}
</Form.Item>
</td>
);

View File

@@ -1,8 +1,13 @@
import Dinero from "dinero.js";
export const CalculateBillTotal = (invoice) => {
const { total, billlines, federal_tax_rate, local_tax_rate, state_tax_rate } =
invoice;
const {
total,
billlines,
federal_tax_rate,
local_tax_rate,
state_tax_rate,
} = invoice;
//TODO Determine why this recalculates so many times.
let subtotal = Dinero({ amount: 0 });
@@ -15,7 +20,8 @@ export const CalculateBillTotal = (invoice) => {
billlines.forEach((i) => {
if (!!i) {
const itemTotal = Dinero({
amount: Math.round((i.actual_cost || 0) * 100),
amount:
Math.round(((i.actual_cost || 0) * 100 + Number.EPSILON) * 100) / 100,
}).multiply(i.quantity || 1);
subtotal = subtotal.add(itemTotal);

View File

@@ -12,18 +12,7 @@ const BillLineSearchSelect = ({ options, disabled, ...restProps }, ref) => {
disabled={disabled}
ref={ref}
showSearch
// optionFilterProp="line_desc"
filterOption={(inputValue, option) => {
return (
(option.line_desc &&
option.line_desc
.toLowerCase()
.includes(inputValue.toLowerCase())) ||
(option.oem_partno &&
option.oem_partno.toLowerCase().includes(inputValue.toLowerCase()))
);
}}
notFoundContent={"Removed."}
optionFilterProp="line_desc"
{...restProps}
>
<Select.Option key={null} value={"noline"} cost={0} line_desc={""}>
@@ -32,19 +21,14 @@ const BillLineSearchSelect = ({ options, disabled, ...restProps }, ref) => {
{options
? options.map((item) => (
<Option
disabled={item.removed}
key={item.id}
value={item.id}
cost={item.act_price ? item.act_price : 0}
part_type={item.part_type}
line_desc={item.line_desc}
part_qty={item.part_qty}
oem_partno={item.oem_partno}
style={{
...(item.removed ? { textDecoration: "line-through" } : {}),
}}
>
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
{`${item.line_desc}${
item.oem_partno ? ` - ${item.oem_partno}` : ""
}`}
</Option>

View File

@@ -47,9 +47,7 @@ export function BillMarkForReexportButton({ bodyshop, authLevel, bill }) {
});
if (!result.errors) {
notification["success"]({
message: t("bills.successes.reexport"),
});
notification["success"]({ message: t("bills.successes.save") });
} else {
notification["error"]({
message: t("bills.errors.saving", {

View File

@@ -1,11 +1,10 @@
import { EditFilled, SyncOutlined } from "@ant-design/icons";
import { EyeFilled, SyncOutlined } from "@ant-design/icons";
import { Button, Card, Checkbox, Input, Space, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters";
@@ -15,7 +14,6 @@ import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
const mapStateToProps = createStructuredSelector({
//jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
@@ -28,7 +26,6 @@ const mapDispatchToProps = (dispatch) => ({
});
export function BillsListTableComponent({
bodyshop,
job,
billsQuery,
handleOnRowClick,
@@ -50,14 +47,12 @@ export function BillsListTableComponent({
<Space wrap>
{showView && (
<Button onClick={() => handleOnRowClick(record)}>
<EditFilled />
<EyeFilled />
</Button>
)}
<BillDeleteButton bill={record} />
<Button
disabled={
record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid
}
disabled={record.is_credit_memo}
onClick={() =>
setPartsOrderContext({
actions: {},

View File

@@ -1,11 +1,10 @@
import { HomeFilled } from "@ant-design/icons";
import { Breadcrumb, Row, Col } from "antd";
import { Breadcrumb } from "antd";
import React from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { selectBreadcrumbs } from "../../redux/application/application.selectors";
import GlobalSearch from "../global-search/global-search.component";
import "./breadcrumbs.styles.scss";
const mapStateToProps = createStructuredSelector({
@@ -14,29 +13,24 @@ const mapStateToProps = createStructuredSelector({
export function BreadCrumbs({ breadcrumbs }) {
return (
<Row className="breadcrumb-container">
<Col xs={24} sm={24} md={16}>
<Breadcrumb separator=">">
<Breadcrumb.Item>
<Link to={`/manage`}>
<HomeFilled />
</Link>
</Breadcrumb.Item>
{breadcrumbs.map((item) =>
item.link ? (
<Breadcrumb.Item key={item.label}>
<Link to={item.link}>{item.label} </Link>
</Breadcrumb.Item>
) : (
<Breadcrumb.Item key={item.label}>{item.label}</Breadcrumb.Item>
)
)}
</Breadcrumb>
</Col>
<Col xs={24} sm={24} md={8}>
<GlobalSearch />
</Col>
</Row>
<div className="breadcrumb-container imex-flex-row">
<Breadcrumb separator=">">
<Breadcrumb.Item>
<Link to={`/manage`}>
<HomeFilled />
</Link>
</Breadcrumb.Item>
{breadcrumbs.map((item) =>
item.link ? (
<Breadcrumb.Item key={item.label}>
<Link to={item.link}>{item.label} </Link>
</Breadcrumb.Item>
) : (
<Breadcrumb.Item key={item.label}>{item.label}</Breadcrumb.Item>
)
)}
</Breadcrumb>
</div>
);
}
export default connect(mapStateToProps, null)(BreadCrumbs);

View File

@@ -0,0 +1,43 @@
import { MessageOutlined } from "@ant-design/icons";
import { Badge, Card } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
import ChatPopupComponent from '../chat-popup/chat-popup.component'
const mapStateToProps = createStructuredSelector({
chatVisible: selectChatVisible,
});
const mapDispatchToProps = (dispatch) => ({
toggleChatVisible: () => dispatch(toggleChatVisible()),
});
export function ChatAffixComponent({
chatVisible,
toggleChatVisible,
conversationList,
unreadCount,
}) {
const { t } = useTranslation();
return (
<Badge count={unreadCount}>
<Card size='small'>
{chatVisible ? (
<ChatPopupComponent conversationList={conversationList} />
) : (
<div
onClick={() => toggleChatVisible()}
style={{ cursor: "pointer" }}>
<MessageOutlined />
<strong>{t("messaging.labels.messaging")}</strong>
</div>
)}
</Card>
</Badge>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatAffixComponent);

View File

@@ -1,17 +1,16 @@
import { useApolloClient } from "@apollo/client";
import { getToken, onMessage } from "@firebase/messaging";
import { Button, notification, Space } from "antd";
import axios from "axios";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { useSubscription } from "@apollo/client";
import React from "react";
import { CONVERSATION_LIST_SUBSCRIPTION } from "../../graphql/conversations.queries";
import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import ChatAffixComponent from "./chat-affix.component";
import { Affix } from "antd";
import "./chat-affix.styles.scss";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { messaging, requestForToken } from "../../firebase/firebase.utils";
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import FcmHandler from "../../utils/fcm-handler";
import ChatPopupComponent from "../chat-popup/chat-popup.component";
import "./chat-affix.styles.scss";
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -19,88 +18,35 @@ const mapStateToProps = createStructuredSelector({
});
export function ChatAffixContainer({ bodyshop, chatVisible }) {
const { t } = useTranslation();
const client = useApolloClient();
useEffect(() => {
if (!bodyshop || !bodyshop.messagingservicesid) return;
async function SubscribeToTopic() {
try {
const r = await axios.post("/notifications/subscribe", {
fcm_tokens: await getToken(messaging, {
vapidKey: process.env.REACT_APP_FIREBASE_PUBLIC_VAPID_KEY,
}),
type: "messaging",
imexshopid: bodyshop.imexshopid,
});
console.log("FCM Topic Subscription", r.data);
} catch (error) {
console.log(
"Error attempting to subscribe to messaging topic: ",
error
);
notification.open({
type: "warning",
message: t("general.errors.fcm"),
btn: (
<Space>
<Button
onClick={async () => {
await requestForToken();
SubscribeToTopic();
}}
>
{t("general.actions.tryagain")}
</Button>
<Button
onClick={() => {
const win = window.open(
"https://help.imex.online/en/article/enabling-notifications-o978xi/",
"_blank"
);
win.focus();
}}
>
{t("general.labels.help")}
</Button>
</Space>
),
});
}
const { loading, error, data } = useSubscription(
CONVERSATION_LIST_SUBSCRIPTION,
{
skip: !bodyshop || (bodyshop && !bodyshop.messagingservicesid),
}
);
SubscribeToTopic();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [bodyshop]);
useEffect(() => {
function handleMessage(payload) {
FcmHandler({
client,
payload: (payload && payload.data && payload.data.data) || payload.data,
});
}
let stopMessageListenr, channel;
try {
stopMessageListenr = onMessage(messaging, handleMessage);
channel = new BroadcastChannel("imex-sw-messages");
channel.addEventListener("message", handleMessage);
} catch (error) {
console.log("Unable to set event listeners.");
}
return () => {
stopMessageListenr && stopMessageListenr();
channel && channel.removeEventListener("message", handleMessage);
};
}, [client]);
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />;
if (!bodyshop || !bodyshop.messagingservicesid) return <></>;
return (
<div className={`chat-affix ${chatVisible ? "chat-affix-open" : ""}`}>
{bodyshop && bodyshop.messagingservicesid ? <ChatPopupComponent /> : null}
</div>
<Affix className={`chat-affix ${chatVisible ? "chat-affix-open" : ""}`}>
<div>
{bodyshop && bodyshop.messagingservicesid ? (
<ChatAffixComponent
conversationList={(data && data.conversations) || []}
unreadCount={
(data &&
data.conversations.reduce((acc, val) => {
return (acc = acc + val.messages_aggregate.aggregate.count);
}, 0)) ||
0
}
/>
) : null}
</div>
</Affix>
);
}
export default connect(mapStateToProps, null)(ChatAffixContainer);

View File

@@ -1,11 +1,6 @@
.chat-affix {
position: fixed;
left: 2vw;
position: absolute;
bottom: 2vh;
z-index: 999;
-webkit-box-shadow: 0px 0px 2px 0px rgba(69, 69, 69, 1);
-moz-box-shadow: 0px 0px 2px 0px rgba(69, 69, 69, 1);
box-shadow: 0px 0px 2px 0px rgba(69, 69, 69, 1);
}
.chat-affix-open {

View File

@@ -1,28 +0,0 @@
import { useMutation } from "@apollo/client";
import { Button } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { TOGGLE_CONVERSATION_ARCHIVE } from "../../graphql/conversations.queries";
export default function ChatArchiveButton({ conversation }) {
const [loading, setLoading] = useState(false);
const { t } = useTranslation();
const [updateConversation] = useMutation(TOGGLE_CONVERSATION_ARCHIVE);
const handleToggleArchive = async () => {
setLoading(true);
await updateConversation({
variables: { id: conversation.id, archived: !conversation.archived },
});
setLoading(false);
};
return (
<Button onClick={handleToggleArchive} loading={loading} type="primary">
{conversation.archived
? t("messaging.labels.unarchive")
: t("messaging.labels.archive")}
</Button>
);
}

View File

@@ -1,12 +1,13 @@
import { Badge, List, Tag } from "antd";
import { Badge, List, Tag, Tooltip } from "antd";
import { AlertFilled } from "@ant-design/icons";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setSelectedConversation } from "../../redux/messaging/messaging.actions";
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
import { TimeAgoFormatter } from "../../utils/DateFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter";
import "./chat-conversation-list.styles.scss";
import { useTranslation } from "react-i18next";
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
@@ -22,6 +23,8 @@ export function ChatConversationListComponent({
selectedConversation,
setSelectedConversation,
}) {
const { t } = useTranslation();
return (
<div className="chat-list-container">
<List
@@ -29,7 +32,6 @@ export function ChatConversationListComponent({
dataSource={conversationList}
renderItem={(item) => (
<List.Item
key={item.id}
onClick={() => setSelectedConversation(item.id)}
className={`chat-list-item ${
item.id === selectedConversation
@@ -40,26 +42,31 @@ export function ChatConversationListComponent({
{item.job_conversations.length > 0 ? (
<div className="chat-name">
{item.job_conversations.map((j, idx) => (
<div key={idx}>{`${j.job.ownr_fn || ""} ${
j.job.ownr_ln || ""
} ${j.job.ownr_co_nm || ""} `}</div>
<div key={idx} style={{ display: "flex" }}>
{j.job.owner && !j.job.owner.allow_text_message && (
<Tooltip title={t("messaging.labels.noallowtxt")}>
<AlertFilled
className="production-alert"
style={{ marginRight: ".3rem", alignItems: "center" }}
/>
</Tooltip>
)}
<div>{`${j.job.ownr_fn || ""} ${j.job.ownr_ln || ""} ${
j.job.ownr_co_nm || ""
} `}</div>
</div>
))}
</div>
) : (
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
)}
<div sryle={{ display: "inline-block" }}>
<div>
{item.job_conversations.length > 0
? item.job_conversations.map((j, idx) => (
<Tag key={idx} className="ro-number-tag">
{j.job.ro_number}
</Tag>
))
: null}
</div>
<TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>
</div>
{item.job_conversations.length > 0
? item.job_conversations.map((j, idx) => (
<Tag key={idx} className="ro-number-tag">
{j.job.ro_number}
</Tag>
))
: null}
<Badge count={item.messages_aggregate.aggregate.count || 0} />
</List.Item>
)}

View File

@@ -1,23 +1,24 @@
import { Space } from "antd";
import React from "react";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import ChatArchiveButton from "../chat-archive-button/chat-archive-button.component";
import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.component";
import ChatTagRoContainer from "../chat-tag-ro/chat-tag-ro.container";
export default function ChatConversationTitle({ conversation }) {
return (
<Space wrap>
<PhoneNumberFormatter>
{conversation && conversation.phone_num}
</PhoneNumberFormatter>
<ChatConversationTitleTags
jobConversations={
(conversation && conversation.job_conversations) || []
}
/>
<ChatTagRoContainer conversation={conversation || []} />
<ChatArchiveButton conversation={conversation} />
</Space>
<div>
<div className="imex-flex-row">
<ChatConversationTitleTags
jobConversations={
(conversation && conversation.job_conversations) || []
}
/>
<ChatTagRoContainer conversation={conversation || []} />
</div>
<div className="imex-flex-row">
<PhoneNumberFormatter>
{conversation && conversation.phone_num}
</PhoneNumberFormatter>
</div>
</div>
);
}

View File

@@ -9,7 +9,6 @@ import "./chat-conversation.styles.scss";
export default function ChatConversationComponent({
subState,
conversation,
messages,
handleMarkConversationAsRead,
}) {
const [loading, error] = subState;
@@ -17,6 +16,8 @@ export default function ChatConversationComponent({
if (loading) return <LoadingSkeleton />;
if (error) return <AlertComponent message={error.message} type="error" />;
const messages = (conversation && conversation.messages) || [];
return (
<div
className="chat-conversation"

View File

@@ -1,32 +1,18 @@
import { useMutation, useQuery, useSubscription } from "@apollo/client";
import { useMutation, useSubscription } from "@apollo/client";
import React, { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
CONVERSATION_SUBSCRIPTION_BY_PK,
GET_CONVERSATION_DETAILS,
} from "../../graphql/conversations.queries";
import { CONVERSATION_SUBSCRIPTION_BY_PK } from "../../graphql/conversations.queries";
import { MARK_MESSAGES_AS_READ_BY_CONVERSATION } from "../../graphql/messages.queries";
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
import ChatConversationComponent from "./chat-conversation.component";
import axios from "axios";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
bodyshop: selectBodyshop,
});
export default connect(mapStateToProps, null)(ChatConversationContainer);
export function ChatConversationContainer({ bodyshop, selectedConversation }) {
const {
loading: convoLoading,
error: convoError,
data: convoData,
} = useQuery(GET_CONVERSATION_DETAILS, {
variables: { conversationId: selectedConversation },
});
export function ChatConversationContainer({ selectedConversation }) {
const { loading, error, data } = useSubscription(
CONVERSATION_SUBSCRIPTION_BY_PK,
{
@@ -40,46 +26,30 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
MARK_MESSAGES_AS_READ_BY_CONVERSATION,
{
variables: { conversationId: selectedConversation },
update(cache) {
cache.modify({
id: cache.identify({
__typename: "conversations",
id: selectedConversation,
}),
fields: {
messages_aggregate(cached) {
return { aggregate: { count: 0 } };
},
},
});
},
}
);
const unreadCount =
data &&
data.messages &&
data.messages.reduce((acc, val) => {
return !val.read && !val.isoutbound ? acc + 1 : acc;
}, 0);
(data &&
data.conversations_by_pk &&
data.conversations_by_pk &&
data.conversations_by_pk.messages_aggregate &&
data.conversations_by_pk.messages_aggregate.aggregate &&
data.conversations_by_pk.messages_aggregate.aggregate.count) ||
0;
const handleMarkConversationAsRead = async () => {
if (unreadCount > 0 && !!selectedConversation && !markingAsReadInProgress) {
setMarkingAsReadInProgress(true);
await markConversationRead({});
await axios.post("/sms/markConversationRead", {
conversationid: selectedConversation,
imexshopid: bodyshop.imexshopid,
});
await markConversationRead();
setMarkingAsReadInProgress(false);
}
};
return (
<ChatConversationComponent
subState={[loading || convoLoading, error || convoError]}
conversation={convoData ? convoData.conversations_by_pk : {}}
messages={data ? data.messages : []}
subState={[loading, error]}
conversation={data ? data.conversations_by_pk : {}}
handleMarkConversationAsRead={handleMarkConversationAsRead}
/>
);

View File

@@ -1,5 +1,4 @@
import Icon from "@ant-design/icons";
import { Tooltip } from "antd";
import i18n from "i18next";
import moment from "moment";
import React, { useEffect, useRef } from "react";
@@ -10,7 +9,6 @@ import {
CellMeasurerCache,
List,
} from "react-virtualized";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import "./chat-message-list.styles.scss";
export default function ChatMessageListComponent({ messages }) {
@@ -87,22 +85,17 @@ export default function ChatMessageListComponent({ messages }) {
const MessageRender = (message) => {
return (
<Tooltip title={DateTimeFormatter({ children: message.created_at })}>
<div>
{message.image_path &&
message.image_path.map((i, idx) => (
<div
key={idx}
style={{ display: "flex", justifyContent: "center" }}
>
<a href={i} target="__blank">
<img alt="Received" className="message-img" src={i} />
</a>
</div>
))}
<div>{message.text}</div>
</div>
</Tooltip>
<div>
{message.image_path &&
message.image_path.map((i, idx) => (
<div key={idx} style={{ display: "flex", justifyContent: "center" }}>
<a href={i} target="__blank">
<img alt="Received" className="message-img" src={i} />
</a>
</div>
))}
<div>{message.text}</div>
</div>
);
};

View File

@@ -20,6 +20,7 @@ export function ChatNewConversation({ openChatByPhone }) {
const { t } = useTranslation();
const [form] = Form.useForm();
const handleFinish = (values) => {
console.log("values :>> ", values);
openChatByPhone({ phone_num: values.phoneNumber });
form.resetFields();
};
@@ -46,7 +47,7 @@ export function ChatNewConversation({ openChatByPhone }) {
return (
<Popover trigger="click" content={popContent}>
<PlusCircleFilled />
<PlusCircleFilled style={{ margin: "1rem" }} />
</Popover>
);
}

View File

@@ -1,119 +1,51 @@
import {
InfoCircleOutlined,
MessageOutlined,
ShrinkOutlined,
SyncOutlined,
} from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
import React, { useEffect, useState } from "react";
import { ShrinkOutlined } from "@ant-design/icons";
import { Col, Row, Typography } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { CONVERSATION_LIST_QUERY } from "../../graphql/conversations.queries";
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
import {
selectChatVisible,
selectSelectedConversation,
} from "../../redux/messaging/messaging.selectors";
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
import ChatConversationContainer from "../chat-conversation/chat-conversation.container";
import ChatNewConversation from "../chat-new-conversation/chat-new-conversation.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
import "./chat-popup.styles.scss";
import ChatNewConversation from "../chat-new-conversation/chat-new-conversation.component";
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
chatVisible: selectChatVisible,
});
const mapDispatchToProps = (dispatch) => ({
toggleChatVisible: () => dispatch(toggleChatVisible()),
});
export function ChatPopupComponent({
chatVisible,
conversationList,
selectedConversation,
toggleChatVisible,
}) {
const { t } = useTranslation();
const [pollInterval, setpollInterval] = useState(0);
const { loading, data, refetch, called } = useQuery(CONVERSATION_LIST_QUERY, {
...(pollInterval > 0 ? { pollInterval } : {}),
});
const fcmToken = sessionStorage.getItem("fcmtoken");
useEffect(() => {
if (fcmToken) {
setpollInterval(0);
} else {
setpollInterval(60000);
}
}, [fcmToken]);
useEffect(() => {
if (called && chatVisible) refetch();
}, [chatVisible, called, refetch]);
const unreadCount = data
? data.conversations.reduce(
(acc, val) => val.messages_aggregate.aggregate.count + acc,
0
)
: 0;
return (
<Badge count={unreadCount}>
<Card size="small">
{chatVisible ? (
<div className="chat-popup">
<Space align="center">
<Typography.Title level={4}>
{t("messaging.labels.messaging")}
</Typography.Title>
<ChatNewConversation />
<Tooltip title={t("messaging.labels.recentonly")}>
<InfoCircleOutlined />
</Tooltip>
<SyncOutlined
style={{ cursor: "pointer" }}
onClick={() => refetch()}
/>
{pollInterval > 0 && (
<Tag color="yellow">{t("messaging.labels.nopush")}</Tag>
)}
</Space>
<ShrinkOutlined
onClick={() => toggleChatVisible()}
style={{ position: "absolute", right: ".5rem", top: ".5rem" }}
/>
<div className="chat-popup">
<div style={{ display: "flex", alignItems: "center" }}>
<Typography.Title level={4}>
{t("messaging.labels.messaging")}
</Typography.Title>
<ChatNewConversation />
</div>
<ShrinkOutlined
onClick={() => toggleChatVisible()}
style={{ position: "absolute", right: ".5rem", top: ".5rem" }}
/>
<Row gutter={[8, 8]} className="chat-popup-content">
<Col span={8}>
{loading ? (
<LoadingSpinner />
) : (
<ChatConversationListComponent
conversationList={data ? data.conversations : []}
/>
)}
</Col>
<Col span={16}>
{selectedConversation ? <ChatConversationContainer /> : null}
</Col>
</Row>
</div>
) : (
<div
onClick={() => toggleChatVisible()}
style={{ cursor: "pointer" }}
>
<MessageOutlined />
<strong>{t("messaging.labels.messaging")}</strong>
</div>
)}
</Card>
</Badge>
<Row gutter={[8, 8]} className="chat-popup-content">
<Col span={8}>
<ChatConversationListComponent conversationList={conversationList} />
</Col>
<Col span={16}>
{selectedConversation ? <ChatConversationContainer /> : null}
</Col>
</Row>
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatPopupComponent);

View File

@@ -1,5 +1,5 @@
import { CloseCircleOutlined, LoadingOutlined } from "@ant-design/icons";
import { Select, Empty, Space } from "antd";
import { Select, Empty } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
@@ -13,27 +13,27 @@ export default function ChatTagRoComponent({
const { t } = useTranslation();
return (
<Space flex>
<div style={{ width: "15rem" }}>
<Select
showSearch
autoFocus
dropdownMatchSelectWidth
placeholder={t("general.labels.search")}
filterOption={false}
onSearch={handleSearch}
onSelect={handleInsertTag}
notFoundContent={loading ? <LoadingOutlined /> : <Empty />}
>
{roOptions.map((item, idx) => (
<Select.Option key={item.id || idx}>
{` ${item.ro_number || ""} | ${item.ownr_fn || ""} ${
item.ownr_ln || ""
} ${item.ownr_co_nm || ""}`}
</Select.Option>
))}
</Select>
</div>
<div>
<Select
showSearch
autoFocus
style={{
width: 300,
}}
placeholder={t("general.labels.search")}
filterOption={false}
onSearch={handleSearch}
onSelect={handleInsertTag}
notFoundContent={loading ? <LoadingOutlined /> : <Empty />}
>
{roOptions.map((item, idx) => (
<Select.Option key={item.id || idx}>
{` ${item.ro_number || ""} | ${item.ownr_fn || ""} ${
item.ownr_ln || ""
} ${item.ownr_co_nm || ""}`}
</Select.Option>
))}
</Select>
{loading ? <LoadingOutlined /> : null}
{loading ? (
@@ -41,6 +41,6 @@ export default function ChatTagRoComponent({
) : (
<CloseCircleOutlined onClick={() => setVisible(false)} />
)}
</Space>
</div>
);
}

View File

@@ -53,7 +53,6 @@ export function ContractConvertToRo({
const billingLines = [];
if (contractLength > 0)
billingLines.push({
manual_line:true,
unq_seq: 1,
line_no: 1,
line_ref: 1,
@@ -71,7 +70,6 @@ export function ContractConvertToRo({
contract.kmend - contract.kmstart - contract.dailyfreekm * contractLength;
if (mileageDiff > 0) {
billingLines.push({
manual_line:true,
unq_seq: 2,
line_no: 2,
line_ref: 2,
@@ -88,7 +86,6 @@ export function ContractConvertToRo({
if (values.refuelqty > 0) {
billingLines.push({
manual_line:true,
unq_seq: 3,
line_no: 3,
line_ref: 3,
@@ -104,7 +101,6 @@ export function ContractConvertToRo({
}
if (values.applyCleanupCharge) {
billingLines.push({
manual_line:true,
unq_seq: 4,
line_no: 4,
line_ref: 4,
@@ -121,7 +117,6 @@ export function ContractConvertToRo({
if (contract.damagewaiver) {
//Add for cleanup fee.
billingLines.push({
manual_line:true,
unq_seq: 5,
line_no: 5,
line_ref: 5,

View File

@@ -42,6 +42,7 @@ export default function ContractFormComponent({
<ContractStatusSelector />
</Form.Item>
)}
<Form.Item
label={t("contracts.fields.start")}
name="start"

View File

@@ -17,7 +17,7 @@ export function PartsReceiveModalComponent({ bodyshop, form }) {
return (
<div>
<Form.Item name="plate" label={t("courtesycars.fields.plate")}>
<Form.Item name="fleet" label={t("courtesycars.fields.fleetnumber")}>
<Input />
</Form.Item>
<Form.Item

View File

@@ -44,8 +44,8 @@ export function ContractsFindModalContainer({
callSearch({
variables: {
plate:
(values.plate && values.plate !== "" && values.plate) || undefined,
fleet:
(values.fleet && values.fleet !== "" && values.fleet) || undefined,
time: values.time,
},
});

View File

@@ -83,10 +83,8 @@ export function ContractsList({
render: (text, record) => (
<Link to={`/manage/courtesycars/${record.courtesycar.id}`}>{`${
record.courtesycar.year
} ${record.courtesycar.make} ${record.courtesycar.model}${
record.courtesycar.plate ? ` (${record.courtesycar.plate})` : ""
}${
record.courtesycar.fleetnumber ? ` (${record.courtesycar.fleetnumber})` : ""
} ${record.courtesycar.make} ${record.courtesycar.model} ${
record.courtesycar.plate ? `(${record.courtesycar.plate})` : ""
}`}</Link>
),
},

View File

@@ -31,9 +31,6 @@ const CourtesyCarStatusComponent = ({ value, onChange }, ref) => {
<Option value="courtesycars.status.out">
{t("courtesycars.status.out")}
</Option>
<Option value="courtesycars.status.sold">
{t("courtesycars.status.sold")}
</Option>
</Select>
);
};

View File

@@ -45,10 +45,6 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
text: t("courtesycars.status.out"),
value: "courtesycars.status.out",
},
{
text: t("courtesycars.status.sold"),
value: "courtesycars.status.sold",
},
],
onFilter: (value, record) => value.includes(record.status),
sortOrder:
@@ -94,12 +90,13 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
// sorter: (a, b) => alphaSort(a.model, b.model),
sortOrder:
state.sortedInfo.columnKey === "model" && state.sortedInfo.order,
render: (text, record) =>
record.cccontracts.length === 1 ? (
<Link to={`/manage/jobs/${record.cccontracts[0].job.id}`}>
{record.cccontracts[0].job.ro_number}
</Link>
) : null,
render: (text, record) => (
<div>
{record.cccontracts.length === 1
? record.cccontracts[0].job.ro_number
: null}
</div>
),
},
];

View File

@@ -1,166 +0,0 @@
import { Card } from "antd";
import _ from "lodash";
import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import {
Bar,
CartesianGrid,
ComposedChart,
Legend,
Line,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import * as Utils from "../../scoreboard-targets-table/scoreboard-targets-table.util";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardMonthlyEmployeeEfficiency({
data,
...cardProps
}) {
const { t } = useTranslation();
if (!data) return null;
if (!data.monthly_employee_efficiency)
return <DashboardRefreshRequired {...cardProps} />;
const ticketsByDate = _.groupBy(data.monthly_employee_efficiency, (item) =>
moment(item.date).format("YYYY-MM-DD")
);
const listOfDays = Utils.ListOfDaysInCurrentMonth();
const chartData = listOfDays.reduce((acc, val) => {
//Sum up the current day.
let dailyHrs;
if (!!ticketsByDate[val]) {
dailyHrs = ticketsByDate[val].reduce(
(dayAcc, dayVal) => {
return {
actual: dayAcc.actual + dayVal.actualhrs,
productive: dayAcc.actual + dayVal.productivehrs,
};
},
{ actual: 0, productive: 0 }
);
} else {
dailyHrs = { actual: 0, productive: 0 };
}
const dailyEfficiency =
((dailyHrs.productive - dailyHrs.actual) / dailyHrs.productive + 1) * 100;
const theValue = {
date: moment(val).format("DD"),
...dailyHrs,
dailyEfficiency: isNaN(dailyEfficiency) ? 0 : dailyEfficiency.toFixed(1),
accActual:
acc.length > 0
? acc[acc.length - 1].accActual + dailyHrs.actual
: dailyHrs.actual,
accProductive:
acc.length > 0
? acc[acc.length - 1].accProductive + dailyHrs.productive
: dailyHrs.productive,
accEfficiency: 0,
};
theValue.accEfficiency = (
((theValue.accProductive - theValue.accActual) /
(theValue.accProductive || 1) +
1) *
100
).toFixed(1);
return [...acc, theValue];
}, []);
return (
<Card
title={t("dashboard.titles.monthlyemployeeefficiency")}
{...cardProps}
>
<div style={{ height: "100%" }}>
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
data={chartData}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid stroke="#f5f5f5" />
<XAxis dataKey="date" />
<YAxis
yAxisId="left"
orientation="left"
stroke="#8884d8"
unit=" hrs"
/>
<YAxis
yAxisId="right"
orientation="right"
stroke="#82ca9d"
unit="%"
/>
<Tooltip />
<Legend />
<Line
yAxisId="right"
name="Accumulated Efficiency"
type="monotone"
unit="%"
dataKey="accEfficiency"
stroke="#152228"
connectNulls
// activeDot={{ r: 8 }}
/>
<Line
name="Daily Efficiency"
yAxisId="right"
unit="%"
type="monotone"
connectNulls
dataKey="dailyEfficiency"
stroke="#d31717"
/>
<Bar
name="Actual Hours"
dataKey="actual"
yAxisId="left"
unit=" hrs"
//stackId="day"
barSize={20}
fill="#102568"
/>
<Bar
name="Productive Hours"
dataKey="productive"
yAxisId="left"
unit=" hrs"
//stackId="day"
barSize={20}
fill="#017664"
/>
</ComposedChart>
</ResponsiveContainer>
</div>
</Card>
);
}
export const DashboardMonthlyEmployeeEfficiencyGql = `
monthly_employee_efficiency: timetickets(where: {_and: [{date: {_gte: "${moment()
.startOf("month")
.format("YYYY-MM-DD")}"}},{date: {_lte: "${moment()
.endOf("month")
.format("YYYY-MM-DD")}"}} ]}) {
actualhrs
productivehrs
employeeid
employee {
first_name
last_name
}
date
}
`;

View File

@@ -1,163 +0,0 @@
import { Card, Input, Space, Table, Typography } from "antd";
import axios from "axios";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { alphaSort } from "../../../utils/sorters";
import LoadingSkeleton from "../../loading-skeleton/loading-skeleton.component";
import Dinero from "dinero.js";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardMonthlyJobCosting({ data, ...cardProps }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [costingData, setcostingData] = useState(null);
const [searchText, setSearchText] = useState("");
const [state, setState] = useState({
sortedInfo: {},
});
useEffect(() => {
async function getCostingData() {
if (data && data.monthly_sales) {
setLoading(true);
const response = await axios.post("/job/costingmulti", {
jobids: data.monthly_sales.map((x) => x.id),
});
setcostingData(response.data);
setLoading(false);
}
}
getCostingData();
}, [data]);
if (!data) return null;
if (!data.monthly_sales) return <DashboardRefreshRequired {...cardProps} />;
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const columns = [
{
title: t("bodyshop.fields.responsibilitycenter"),
dataIndex: "cost_center",
key: "cost_center",
sorter: (a, b) => alphaSort(a.cost_center, b.cost_center),
sortOrder:
state.sortedInfo.columnKey === "cost_center" && state.sortedInfo.order,
},
{
title: t("jobs.labels.sales"),
dataIndex: "sales",
key: "sales",
sorter: (a, b) =>
parseFloat(a.sales.substring(1)) - parseFloat(b.sales.substring(1)),
sortOrder:
state.sortedInfo.columnKey === "sales" && state.sortedInfo.order,
},
{
title: t("jobs.labels.costs"),
dataIndex: "costs",
key: "costs",
sorter: (a, b) =>
parseFloat(a.costs.substring(1)) - parseFloat(b.costs.substring(1)),
sortOrder:
state.sortedInfo.columnKey === "costs" && state.sortedInfo.order,
},
{
title: t("jobs.labels.gpdollars"),
dataIndex: "gpdollars",
key: "gpdollars",
sorter: (a, b) =>
parseFloat(a.gpdollars.substring(1)) -
parseFloat(b.gpdollars.substring(1)),
sortOrder:
state.sortedInfo.columnKey === "gpdollars" && state.sortedInfo.order,
},
{
title: t("jobs.labels.gppercent"),
dataIndex: "gppercent",
key: "gppercent",
sorter: (a, b) =>
parseFloat(a.gppercent.slice(0, -1) || 0) -
parseFloat(b.gppercent.slice(0, -1) || 0),
sortOrder:
state.sortedInfo.columnKey === "gppercent" && state.sortedInfo.order,
},
];
const filteredData =
searchText === ""
? (costingData && costingData.allCostCenterData) || []
: costingData.allCostCenterData.filter((d) =>
(d.cost_center || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase())
);
return (
<Card
title={t("dashboard.titles.monthlyjobcosting")}
extra={
<Space wrap>
<Input.Search
placeholder={t("general.labels.search")}
value={searchText}
onChange={(e) => {
e.preventDefault();
setSearchText(e.target.value);
}}
/>
</Space>
}
{...cardProps}
>
<LoadingSkeleton loading={loading}>
<div style={{ height: "100%" }}>
<Table
onChange={handleTableChange}
pagination={{ position: "top", defaultPageSize: 50 }}
columns={columns}
scroll={{ x: true, y: "calc(100% - 4em)" }}
rowKey="id"
style={{ height: "100%" }}
dataSource={filteredData}
summary={() => (
<Table.Summary.Row>
<Table.Summary.Cell>
<Typography.Title level={4}>
{t("general.labels.totals")}
</Typography.Title>
</Table.Summary.Cell>
<Table.Summary.Cell>
{Dinero(
costingData &&
costingData.allSummaryData &&
costingData.allSummaryData.totalSales
).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell>
{Dinero(
costingData &&
costingData.allSummaryData &&
costingData.allSummaryData.totalCost
).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell>
{Dinero(
costingData &&
costingData.allSummaryData &&
costingData.allSummaryData.gpdollars
).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
</Table.Summary.Row>
)}
/>
</div>
</LoadingSkeleton>
</Card>
);
}

View File

@@ -1,163 +0,0 @@
import { Card } from "antd";
import Dinero from "dinero.js";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Cell, Pie, PieChart, ResponsiveContainer, Sector } from "recharts";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardMonthlyLaborSales({ data, ...cardProps }) {
const { t } = useTranslation();
const [activeIndex, setActiveIndex] = useState(0);
if (!data) return null;
if (!data.monthly_sales) return <DashboardRefreshRequired {...cardProps} />;
const laborData = {};
data.monthly_sales.forEach((job) => {
job.joblines.forEach((jobline) => {
if (!jobline.mod_lbr_ty) return;
if (!laborData[jobline.mod_lbr_ty])
laborData[jobline.mod_lbr_ty] = Dinero();
laborData[jobline.mod_lbr_ty] = laborData[jobline.mod_lbr_ty].add(
Dinero({
amount: Math.round(
(job[`rate_${jobline.mod_lbr_ty.toLowerCase()}`] || 0) * 100
),
}).multiply(jobline.mod_lb_hrs || 0)
);
});
});
const chartData = Object.keys(laborData).map((key) => {
return {
name: t(`joblines.fields.lbr_types.${key.toUpperCase()}`),
value: laborData[key].getAmount() / 100,
color: pieColor(key.toUpperCase()),
};
});
return (
<Card title={t("dashboard.titles.monthlylaborsales")} {...cardProps}>
<div style={{ height: "100%" }}>
<ResponsiveContainer width="100%" height="100%">
<PieChart margin={0} padding={0}>
<Pie
data={chartData}
activeIndex={activeIndex}
activeShape={renderActiveShape}
cx="50%"
cy="50%"
innerRadius="60%"
// outerRadius={80}
fill="#8884d8"
dataKey="value"
onMouseEnter={(throwaway, index) => setActiveIndex(index)}
>
{chartData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</ResponsiveContainer>
</div>
</Card>
);
}
export const DashboardMonthlyRevenueGraphGql = `
`;
const pieColor = (type) => {
if (type === "LAA") return "lightgreen";
else if (type === "LAB") return "dodgerblue";
else if (type === "LAD") return "aliceblue";
else if (type === "LAE") return "seafoam";
else if (type === "LAG") return "chartreuse";
else if (type === "LAF") return "magenta";
else if (type === "LAM") return "gold";
else if (type === "LAR") return "crimson";
else if (type === "LAU") return "slategray";
else if (type === "LA1") return "slategray";
else if (type === "LA2") return "slategray";
else if (type === "LA3") return "slategray";
else if (type === "LA4") return "slategray";
return "slategray";
};
const renderActiveShape = (props) => {
//const RADIAN = Math.PI / 180;
const {
cx,
cy,
//midAngle,
innerRadius,
outerRadius,
startAngle,
endAngle,
fill,
payload,
// percent,
value,
} = props;
// const sin = Math.sin(-RADIAN * midAngle);
// const cos = Math.cos(-RADIAN * midAngle);
// // const sx = cx + (outerRadius + 10) * cos;
// const sy = cy + (outerRadius + 10) * sin;
// const mx = cx + (outerRadius + 30) * cos;
// const my = cy + (outerRadius + 30) * sin;
// //const ex = mx + (cos >= 0 ? 1 : -1) * 22;
// const ey = my;
//const textAnchor = cos >= 0 ? "start" : "end";
return (
<g>
<text x={cx} y={cy} dy={0} textAnchor="middle" fill={fill}>
{payload.name}
</text>
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
</text>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius}
startAngle={startAngle}
endAngle={endAngle}
fill={fill}
/>
<Sector
cx={cx}
cy={cy}
startAngle={startAngle}
endAngle={endAngle}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
</g>
);
};
// <path
// d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`}
// stroke={fill}
// fill="none"
// />;
// <text
// x={ex + (cos >= 0 ? 1 : -1) * 12}
// y={ey}
// textAnchor={textAnchor}
// fill="#333"
// >
// {payload.name}
// </text>
// <text
// x={ex + (cos >= 0 ? 1 : -1) * 12}
// y={ey}
// dy={18}
// textAnchor={textAnchor}
// fill="#999"
// >
// {Dinero({ amount: Math.round(value * 100) }).toFormat()}
// </text>

View File

@@ -1,136 +0,0 @@
import { Card } from "antd";
import Dinero from "dinero.js";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Cell, Pie, PieChart, ResponsiveContainer, Sector } from "recharts";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardMonthlyPartsSales({ data, ...cardProps }) {
const { t } = useTranslation();
const [activeIndex, setActiveIndex] = useState(0);
if (!data) return null;
if (!data.monthly_sales) return <DashboardRefreshRequired {...cardProps} />;
const partData = {};
data.monthly_sales.forEach((job) => {
job.joblines.forEach((jobline) => {
if (!jobline.part_type) return;
if (!partData[jobline.part_type]) partData[jobline.part_type] = Dinero();
partData[jobline.part_type] = partData[jobline.part_type].add(
Dinero({ amount: Math.round((jobline.act_price || 0) * 100) }).multiply(
jobline.part_qty || 0
)
);
});
});
const chartData = Object.keys(partData).map((key) => {
return {
name: t(`joblines.fields.part_types.${key.toUpperCase()}`),
value: partData[key].getAmount() / 100,
color: pieColor(key.toUpperCase()),
};
});
return (
<Card title={t("dashboard.titles.monthlypartssales")} {...cardProps}>
<div style={{ height: "100%" }}>
<ResponsiveContainer width="100%" height="100%">
<PieChart margin={0} padding={0}>
<Pie
data={chartData}
activeIndex={activeIndex}
activeShape={renderActiveShape}
cx="50%"
cy="50%"
innerRadius="60%"
// outerRadius={80}
fill="#8884d8"
dataKey="value"
onMouseEnter={(throwaway, index) => setActiveIndex(index)}
>
{chartData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</ResponsiveContainer>
</div>
</Card>
);
}
export const DashboardMonthlyRevenueGraphGql = `
`;
const pieColor = (type) => {
if (type === "PAA") return "darkgreen";
else if (type === "PAC") return "green";
else if (type === "PAE") return "gold";
else if (type === "PAG") return "seafoam";
else if (type === "PAL") return "chartreuse";
else if (type === "PAM") return "magenta";
else if (type === "PAN") return "crimson";
else if (type === "PAO") return "gold";
else if (type === "PAP") return "crimson";
else if (type === "PAR") return "indigo";
else if (type === "PAS") return "dodgerblue";
else if (type === "PASL") return "dodgerblue";
return "slategray";
};
const renderActiveShape = (props) => {
// const RADIAN = Math.PI / 180;
const {
cx,
cy,
// midAngle,
innerRadius,
outerRadius,
startAngle,
endAngle,
fill,
payload,
// percent,
value,
} = props;
// const sin = Math.sin(-RADIAN * midAngle);
// const cos = Math.cos(-RADIAN * midAngle);
// const sx = cx + (outerRadius + 10) * cos;
//const sy = cy + (outerRadius + 10) * sin;
// const mx = cx + (outerRadius + 30) * cos;
//const my = cy + (outerRadius + 30) * sin;
// const ex = mx + (cos >= 0 ? 1 : -1) * 22;
// const ey = my;
// const textAnchor = cos >= 0 ? "start" : "end";
return (
<g>
<text x={cx} y={cy} dy={0} textAnchor="middle" fill={fill}>
{payload.name}
</text>
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
</text>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius}
startAngle={startAngle}
endAngle={endAngle}
fill={fill}
/>
<Sector
cx={cx}
cy={cy}
startAngle={startAngle}
endAngle={endAngle}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
</g>
);
};

View File

@@ -2,30 +2,29 @@ import { Card } from "antd";
import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import _ from "lodash";
import {
Area,
Bar,
CartesianGrid,
ComposedChart,
Legend,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
Area,
Bar,
CartesianGrid,
ComposedChart,
Legend,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis
} from "recharts";
import Dinero from "dinero.js";
import * as Utils from "../../scoreboard-targets-table/scoreboard-targets-table.util";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardMonthlyRevenueGraph({ data, ...cardProps }) {
const { t } = useTranslation();
if (!data) return null;
if (!data.monthly_sales) return <DashboardRefreshRequired {...cardProps} />;
const jobsByDate = _.groupBy(data.monthly_sales, (item) =>
moment(item.date_invoiced).format("YYYY-MM-DD")
);
const jobsByDate = {
"2020-07-5": [{ clm_total: 1224 }],
"2020-07-8": [{ clm_total: 987 }, { clm_total: 8755 }],
"2020-07-12": [{ clm_total: 684 }, { clm_total: 12022 }],
"2020-07-21": [{ clm_total: 15000 }],
"2020-07-28": [{ clm_total: 122 }, { clm_total: 4522 }],
};
const listOfDays = Utils.ListOfDaysInCurrentMonth();
@@ -34,21 +33,17 @@ export default function DashboardMonthlyRevenueGraph({ data, ...cardProps }) {
let dailySales;
if (!!jobsByDate[val]) {
dailySales = jobsByDate[val].reduce((dayAcc, dayVal) => {
return dayAcc.add(
Dinero((dayVal.job_totals && dayVal.job_totals.totals.subtotal) || 0)
);
}, Dinero());
return dayAcc + dayVal.clm_total;
}, 0);
} else {
dailySales = Dinero();
dailySales = 0;
}
const theValue = {
date: moment(val).format("DD"),
dailySales: dailySales.getAmount() / 100,
date: moment(val).format("D dd"),
dailySales,
accSales:
acc.length > 0
? acc[acc.length - 1].accSales + dailySales.getAmount() / 100
: dailySales.getAmount() / 100,
acc.length > 0 ? acc[acc.length - 1].accSales + dailySales : dailySales,
};
return [...acc, theValue];
@@ -56,40 +51,32 @@ export default function DashboardMonthlyRevenueGraph({ data, ...cardProps }) {
return (
<Card title={t("dashboard.titles.monthlyrevenuegraph")} {...cardProps}>
<div style={{ height: "100%" }}>
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
data={chartData}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid stroke="#f5f5f5" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip
formatter={(value, name, props) => value && value.toFixed(2)}
/>
<Legend />
<Area
type="monotone"
name="Accumulated Sales"
dataKey="accSales"
fill="#3CB371"
stroke="#3CB371"
/>
<Bar
name="Daily Sales"
dataKey="dailySales"
//stackId="day"
barSize={20}
fill="#413ea0"
/>
</ComposedChart>
</ResponsiveContainer>
</div>
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
data={chartData}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid stroke="#f5f5f5" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Legend />
<Area
type="monotone"
name="Accumulated Sales"
dataKey="accSales"
fill="#8884d8"
stroke="#8884d8"
/>
<Bar
name="Daily Sales"
dataKey="dailySales"
//stackId="day"
barSize={20}
fill="#413ea0"
/>
</ComposedChart>
</ResponsiveContainer>
</Card>
);
}
export const DashboardMonthlyRevenueGraphGql = `
`;

View File

@@ -1,47 +1,30 @@
import { ArrowDownOutlined, ArrowUpOutlined } from "@ant-design/icons";
import { Card, Statistic } from "antd";
import Dinero from "dinero.js";
import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardProjectedMonthlySales({ data, ...cardProps }) {
const { t } = useTranslation();
if (!data) return null;
if (!data.projected_monthly_sales)
return <DashboardRefreshRequired {...cardProps} />;
const aboveTargetMonthlySales = false;
const dollars =
data.projected_monthly_sales &&
data.projected_monthly_sales.reduce(
(acc, val) =>
acc.add(
Dinero(
val.job_totals &&
val.job_totals.totals &&
val.job_totals.totals.subtotal
)
),
Dinero()
);
return (
<Card title={t("dashboard.titles.projectedmonthlysales")} {...cardProps}>
<Statistic value={dollars.toFormat()} />
<Card {...cardProps}>
<Statistic
title={t("dashboard.titles.projectedmonthlysales")}
value={222000.0}
precision={2}
prefix={
<div>
{aboveTargetMonthlySales ? (
<ArrowUpOutlined />
) : (
<ArrowDownOutlined />
)}
$
</div>
}
valueStyle={{ color: aboveTargetMonthlySales ? "green" : "red" }}
/>
</Card>
);
}
export const DashboardProjectedMonthlySalesGql = `
projected_monthly_sales: jobs(where: {_or: [{_and: [{date_invoiced: {_gte: "${moment()
.startOf("month")
.format("YYYY-MM-DD")}"}}, {date_invoiced: {_lte: "${moment()
.endOf("month")
.format("YYYY-MM-DD")}"}}]}, {_and: [{scheduled_completion: {_gte: "${moment()
.startOf("month")
.format("YYYY-MM-DD")}"}}, {scheduled_completion: {_lte: "${moment()
.endOf("month")
.format("YYYY-MM-DD")}"}}]}]}) {
id
date_invoiced
job_totals
}
`;

View File

@@ -1,25 +0,0 @@
import { SyncOutlined } from "@ant-design/icons";
import { Card } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
export default function DashboardRefreshRequired(props) {
const { t } = useTranslation();
return (
<Card {...props}>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
textOverflow: "ellipsis",
}}
>
<SyncOutlined style={{ fontSize: "300%", margin: "1rem" }} />
<div>{t("dashboard.errors.refreshrequired")}</div>
</div>
</Card>
);
}

View File

@@ -1,27 +1,33 @@
import { Card, Statistic } from "antd";
import Dinero from "dinero.js";
import React from "react";
import { Card, Statistic } from "antd";
import { useTranslation } from "react-i18next";
import DashboardRefreshRequired from "../refresh-required.component";
import { ArrowDownOutlined, ArrowUpOutlined } from "@ant-design/icons";
export default function DashboardTotalProductionDollars({
data,
...cardProps
}) {
const { t } = useTranslation();
if (!data) return null;
if (!data.production_jobs) return <DashboardRefreshRequired {...cardProps} />;
const dollars =
data.production_jobs &&
data.production_jobs.reduce(
(acc, val) =>
acc.add(Dinero(val.job_totals && val.job_totals.totals.subtotal)),
Dinero()
);
const aboveTargetProductionDollars = false;
return (
<Card title={t("dashboard.labels.dollarsinproduction")} {...cardProps}>
<Statistic value={dollars.toFormat()} />
<Card {...cardProps}>
<Statistic
title={t("dashboard.titles.productiondollars")}
value={175000.0}
precision={2}
prefix={
<div>
{aboveTargetProductionDollars ? (
<ArrowUpOutlined />
) : (
<ArrowDownOutlined />
)}
$
</div>
}
valueStyle={{ color: aboveTargetProductionDollars ? "green" : "red" }}
/>
</Card>
);
}

View File

@@ -1,63 +1,19 @@
import { Card, Space, Statistic } from "antd";
import React from "react";
import { Card, Statistic } from "antd";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../../redux/user/user.selectors";
import DashboardRefreshRequired from "../refresh-required.component";
import { ArrowDownOutlined, ArrowUpOutlined } from "@ant-design/icons";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({});
export default connect(
mapStateToProps,
mapDispatchToProps
)(DashboardTotalProductionHours);
export function DashboardTotalProductionHours({
bodyshop,
data,
...cardProps
}) {
export default function DashboardTotalProductionHours({ data, ...cardProps }) {
const { t } = useTranslation();
if (!data) return null;
if (!data.production_jobs) return <DashboardRefreshRequired {...cardProps} />;
const hours =
data.production_jobs &&
data.production_jobs.reduce(
(acc, val) => {
return {
body: acc.body + val.labhrs.aggregate.sum.mod_lb_hrs,
ref: acc.ref + val.larhrs.aggregate.sum.mod_lb_hrs,
total:
acc.total +
val.labhrs.aggregate.sum.mod_lb_hrs +
val.larhrs.aggregate.sum.mod_lb_hrs,
};
},
{ body: 0, ref: 0, total: 0 }
);
const aboveTargetHours = hours.total >= bodyshop.prodtargethrs;
const aboveTargetHours = true;
return (
<Card {...cardProps} title={t("dashboard.titles.prodhrssummary")}>
<Space wrap style={{ flex: 1 }}>
<Statistic
title={t("dashboard.labels.bodyhrs")}
value={hours.body.toFixed(1)}
/>
<Statistic
title={t("dashboard.labels.refhrs")}
value={hours.ref.toFixed(1)}
/>
<Statistic
title={t("dashboard.labels.prodhrs")}
value={hours.total.toFixed(1)}
valueStyle={{ color: aboveTargetHours ? "green" : "red" }}
/>
</Space>
<Card {...cardProps}>
<Statistic
title={t("dashboard.titles.productionhours")}
value={750}
prefix={aboveTargetHours ? <ArrowUpOutlined /> : <ArrowDownOutlined />}
valueStyle={{ color: aboveTargetHours ? "green" : "red" }}
/>
</Card>
);
}
export const DashboardTotalProductionHoursGql = ``;

View File

@@ -1,352 +1,185 @@
import Icon, { SyncOutlined } from "@ant-design/icons";
import { gql, useMutation, useQuery } from "@apollo/client";
import { Button, Dropdown, Menu, notification, PageHeader, Space } from "antd";
import i18next from "i18next";
import _ from "lodash";
import moment from "moment";
import React, { useState } from "react";
import { Responsive, WidthProvider } from "react-grid-layout";
import { useTranslation } from "react-i18next";
import { MdClose } from "react-icons/md";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import DashboardMonthlyEmployeeEfficiency, {
DashboardMonthlyEmployeeEfficiencyGql,
} from "../dashboard-components/monthly-employee-efficiency/monthly-employee-efficiency.component";
import DashboardMonthlyJobCosting from "../dashboard-components/monthly-job-costing/monthly-job-costing.component";
import DashboardMonthlyLaborSales from "../dashboard-components/monthly-labor-sales/monthly-labor-sales.component";
import DashboardMonthlyPartsSales from "../dashboard-components/monthly-parts-sales/monthly-parts-sales.component";
import DashboardMonthlyRevenueGraph, {
DashboardMonthlyRevenueGraphGql,
} from "../dashboard-components/monthly-revenue-graph/monthly-revenue-graph.component";
import DashboardProjectedMonthlySales, {
DashboardProjectedMonthlySalesGql,
} from "../dashboard-components/pojected-monthly-sales/projected-monthly-sales.component";
import DashboardTotalProductionDollars from "../dashboard-components/total-production-dollars/total-production-dollars.component";
import DashboardTotalProductionHours, {
DashboardTotalProductionHoursGql,
} from "../dashboard-components/total-production-hours/total-production-hours.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
//Combination of the following:
// /node_modules/react-grid-layout/css/styles.css
// /node_modules/react-resizable/css/styles.css
import "./dashboard-grid.styles.scss";
import { GenerateDashboardData } from "./dashboard-grid.utils";
// import Icon from "@ant-design/icons";
// import { Button, Dropdown, Menu, notification } from "antd";
// import React, { useState } from "react";
// import { useMutation, useQuery } from "@apollo/client";
// import { Responsive, WidthProvider } from "react-grid-layout";
// import { useTranslation } from "react-i18next";
// import { MdClose } from "react-icons/md";
// import { connect } from "react-redux";
// import { createStructuredSelector } from "reselect";
// import { logImEXEvent } from "../../firebase/firebase.utils";
// import { QUERY_DASHBOARD_DETAILS } from "../../graphql/bodyshop.queries";
// import { UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
// import {
// selectBodyshop,
// selectCurrentUser,
// } from "../../redux/user/user.selectors";
// import AlertComponent from "../alert/alert.component";
// import DashboardMonthlyRevenueGraph from "../dashboard-components/monthly-revenue-graph/monthly-revenue-graph.component";
// import DashboardProjectedMonthlySales from "../dashboard-components/pojected-monthly-sales/projected-monthly-sales.component";
// import DashboardTotalProductionDollars from "../dashboard-components/total-production-dollars/total-production-dollars.component";
// import DashboardTotalProductionHours from "../dashboard-components/total-production-hours/total-production-hours.component";
// import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
// //Combination of the following:
// // /node_modules/react-grid-layout/css/styles.css
// // /node_modules/react-resizable/css/styles.css
// import "./dashboard-grid.styles.css";
// import "./dashboard-grid.styles.scss";
const ResponsiveReactGridLayout = WidthProvider(Responsive);
// const ResponsiveReactGridLayout = WidthProvider(Responsive);
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
// const mapStateToProps = createStructuredSelector({
// currentUser: selectCurrentUser,
// bodyshop: selectBodyshop,
// });
// const mapDispatchToProps = (dispatch) => ({
// //setUserLanguage: language => dispatch(setUserLanguage(language))
// });
export function DashboardGridComponent({ currentUser, bodyshop }) {
const { t } = useTranslation();
const [state, setState] = useState({
...(bodyshop.associations[0].user.dashboardlayout
? bodyshop.associations[0].user.dashboardlayout
: { items: [], layout: {}, layouts: [] }),
});
// export function DashboardGridComponent({ currentUser, bodyshop }) {
// const { loading, error, data } = useQuery(QUERY_DASHBOARD_DETAILS);
// const { t } = useTranslation();
// const [state, setState] = useState({
// layout: bodyshop.associations[0].user.dashboardlayout || [
// { i: "ProductionDollars", x: 0, y: 0, w: 2, h: 2 },
// // { i: "ProductionHours", x: 2, y: 0, w: 2, h: 2 },
// ],
// });
// const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);
const { loading, error, data, refetch } = useQuery(
createDashboardQuery(state)
);
// const handleLayoutChange = async (newLayout) => {
// logImEXEvent("dashboard_change_layout");
// setState({ ...state, layout: newLayout });
// const result = await updateLayout({
// variables: { email: currentUser.email, layout: newLayout },
// });
const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);
// if (!!result.errors) {
// notification["error"]({
// message: t("dashboard.errors.updatinglayout", {
// message: JSON.stringify(result.errors),
// }),
// });
// }
// };
const handleLayoutChange = async (layout, layouts) => {
logImEXEvent("dashboard_change_layout");
// const handleRemoveComponent = (key) => {
// logImEXEvent("dashboard_remove_component", { name: key });
setState({ ...state, layout, layouts });
// const idxToRemove = state.layout.findIndex((i) => i.i === key);
// const newLayout = state.layout;
// newLayout.splice(idxToRemove, 1);
// handleLayoutChange(newLayout);
// };
const result = await updateLayout({
variables: {
email: currentUser.email,
layout: { ...state, layout, layouts },
},
});
if (!!result.errors) {
notification["error"]({
message: t("dashboard.errors.updatinglayout", {
message: JSON.stringify(result.errors),
}),
});
}
};
const handleRemoveComponent = (key) => {
logImEXEvent("dashboard_remove_component", { name: key });
const idxToRemove = state.items.findIndex((i) => i.i === key);
// const handleAddComponent = (e) => {
// logImEXEvent("dashboard_add_component", { name: e });
const items = _.cloneDeep(state.items);
// handleLayoutChange([
// ...state.layout,
// {
// i: e.key,
// x: (state.layout.length * 2) % (state.cols || 12),
// y: Infinity, // puts it at the bottom
// w: componentList[e.key].w || 2,
// h: componentList[e.key].h || 2,
// },
// ]);
// };
items.splice(idxToRemove, 1);
setState({ ...state, items });
};
// const onBreakpointChange = (breakpoint, cols) => {
// setState({ ...state, breakpoint: breakpoint, cols: cols });
// };
const handleAddComponent = (e) => {
logImEXEvent("dashboard_add_component", { name: e });
setState({
...state,
items: [
...state.items,
{
i: e.key,
x: (state.items.length * 2) % (state.cols || 12),
y: 99, // puts it at the bottom
w: componentList[e.key].w || 2,
h: componentList[e.key].h || 2,
},
],
});
};
// const existingLayoutKeys = state.layout.map((i) => i.i);
// const addComponentOverlay = (
// <Menu onClick={handleAddComponent}>
// {Object.keys(componentList).map((key) => (
// <Menu.Item
// key={key}
// value={key}
// disabled={existingLayoutKeys.includes(key)}
// >
// {componentList[key].label}
// </Menu.Item>
// ))}
// </Menu>
// );
const dashboarddata = React.useMemo(
() => GenerateDashboardData(data),
[data]
);
const existingLayoutKeys = state.items.map((i) => i.i);
const addComponentOverlay = (
<Menu onClick={handleAddComponent}>
{Object.keys(componentList).map((key) => (
<Menu.Item
key={key}
value={key}
disabled={existingLayoutKeys.includes(key)}
>
{componentList[key].label}
</Menu.Item>
))}
</Menu>
);
// if (error) return <AlertComponent message={error.message} type="error" />;
if (error) return <AlertComponent message={error.message} type="error" />;
// return (
// <div>
// <Dropdown overlay={addComponentOverlay} trigger={["click"]}>
// <Button>{t("dashboard.actions.addcomponent")}</Button>
// </Dropdown>
// <ResponsiveReactGridLayout
// className="layout"
// breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
// cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
// width="100%"
// onLayoutChange={handleLayoutChange}
// onBreakpointChange={onBreakpointChange}
// >
// {state.layout.map((item, index) => {
// const TheComponent = componentList[item.i].component;
// return (
// <div key={item.i} data-grid={item}>
// <LoadingSkeleton loading={loading}>
// <Icon
// component={MdClose}
// key={item.i}
// style={{
// position: "absolute",
// zIndex: "2",
// right: ".25rem",
// top: ".25rem",
// cursor: "pointer",
// }}
// onClick={() => handleRemoveComponent(item.i)}
// />
// <TheComponent
// className="dashboard-card"
// size="small"
// style={{ height: "100%", width: "100%" }}
// />
// </LoadingSkeleton>
// </div>
// );
// })}
// </ResponsiveReactGridLayout>
// </div>
// );
// }
return (
<div>
<PageHeader
extra={
<Space>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Dropdown overlay={addComponentOverlay} trigger={["click"]}>
<Button>{t("dashboard.actions.addcomponent")}</Button>
</Dropdown>
</Space>
}
/>
// export default connect(
// mapStateToProps,
// mapDispatchToProps
// )(DashboardGridComponent);
<ResponsiveReactGridLayout
className="layout"
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
width="100%"
layouts={state.layouts}
onLayoutChange={handleLayoutChange}
// onBreakpointChange={onBreakpointChange}
>
{state.items.map((item, index) => {
const TheComponent = componentList[item.i].component;
return (
<div
key={item.i}
data-grid={{
...item,
minH: componentList[item.i].minH || 1,
minW: componentList[item.i].minW || 1,
}}
>
<LoadingSkeleton loading={loading}>
<Icon
component={MdClose}
key={item.i}
style={{
position: "absolute",
zIndex: "2",
right: ".25rem",
top: ".25rem",
cursor: "pointer",
}}
onClick={() => handleRemoveComponent(item.i)}
/>
<TheComponent className="dashboard-card" data={dashboarddata} />
</LoadingSkeleton>
</div>
);
})}
</ResponsiveReactGridLayout>
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(DashboardGridComponent);
const componentList = {
ProductionDollars: {
label: i18next.t("dashboard.titles.productiondollars"),
component: DashboardTotalProductionDollars,
gqlFragment: null,
w: 1,
h: 1,
minW: 2,
minH: 1,
},
ProductionHours: {
label: i18next.t("dashboard.titles.productionhours"),
component: DashboardTotalProductionHours,
gqlFragment: DashboardTotalProductionHoursGql,
w: 3,
h: 1,
minW: 3,
minH: 1,
},
ProjectedMonthlySales: {
label: i18next.t("dashboard.titles.projectedmonthlysales"),
component: DashboardProjectedMonthlySales,
gqlFragment: DashboardProjectedMonthlySalesGql,
w: 2,
h: 1,
minW: 2,
minH: 1,
},
MonthlyRevenueGraph: {
label: i18next.t("dashboard.titles.monthlyrevenuegraph"),
component: DashboardMonthlyRevenueGraph,
gqlFragment: DashboardMonthlyRevenueGraphGql,
w: 4,
h: 2,
minW: 4,
minH: 2,
},
MonthlyJobCosting: {
label: i18next.t("dashboard.titles.monthlyjobcosting"),
component: DashboardMonthlyJobCosting,
gqlFragment: null,
minW: 6,
minH: 3,
w: 6,
h: 3,
},
MonthlyPartsSales: {
label: i18next.t("dashboard.titles.monthlypartssales"),
component: DashboardMonthlyPartsSales,
gqlFragment: null,
minW: 2,
minH: 2,
w: 2,
h: 2,
},
MonthlyLaborSales: {
label: i18next.t("dashboard.titles.monthlylaborsales"),
component: DashboardMonthlyLaborSales,
gqlFragment: null,
minW: 2,
minH: 2,
w: 2,
h: 2,
},
MonthlyEmployeeEfficency: {
label: i18next.t("dashboard.titles.monthlyemployeeefficiency"),
component: DashboardMonthlyEmployeeEfficiency,
gqlFragment: DashboardMonthlyEmployeeEfficiencyGql,
minW: 2,
minH: 2,
w: 2,
h: 2,
},
};
const createDashboardQuery = (state) => {
const componentBasedAdditions =
state &&
Array.isArray(state.layout) &&
state.layout
.map((item, index) => componentList[item.i].gqlFragment || "")
.join("");
return gql`
query QUERY_DASHBOARD_DETAILS {
${componentBasedAdditions || ""}
monthly_sales: jobs(where: {_and: [{date_invoiced: {_gte: "${moment()
.startOf("month")
.format("YYYY-MM-DD")}"}}, {date_invoiced: {_lte: "${moment()
.endOf("month")
.format("YYYY-MM-DD")}"}}]}) {
id
date_invoiced
job_totals
rate_la1
rate_la2
rate_la3
rate_la4
rate_laa
rate_lab
rate_lad
rate_lae
rate_laf
rate_lag
rate_lam
rate_lar
rate_las
rate_lau
rate_ma2s
rate_ma2t
rate_ma3s
rate_mabl
rate_macs
rate_mahw
rate_mapa
rate_mash
rate_matd
joblines(where: { removed: { _eq: false } }) {
id
mod_lbr_ty
mod_lb_hrs
act_price
part_qty
part_type
}
}
production_jobs: jobs(where: { inproduction: { _eq: true } }) {
id
ro_number
ins_co_nm
job_totals
joblines(where: { removed: { _eq: false } }) {
id
mod_lbr_ty
mod_lb_hrs
act_price
part_qty
part_type
}
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" } }) {
aggregate {
sum {
mod_lb_hrs
}
}
}
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" } }) {
aggregate {
sum {
mod_lb_hrs
}
}
}
}
}
`;
};
// const componentList = {
// ProductionDollars: {
// label: "Production Dollars",
// component: DashboardTotalProductionDollars,
// w: 2,
// h: 1,
// },
// ProductionHours: {
// label: "Production Hours",
// component: DashboardTotalProductionHours,
// w: 2,
// h: 1,
// },
// ProjectedMonthlySales: {
// label: "Projected Monthly Sales",
// component: DashboardProjectedMonthlySales,
// w: 2,
// h: 1,
// },
// MonthlyRevenueGraph: {
// label: "Monthly Sales Graph",
// component: DashboardMonthlyRevenueGraph,
// w: 2,
// h: 2,
// },
// };

View File

@@ -0,0 +1,128 @@
.react-resizable {
position: relative;
}
.react-resizable-handle {
position: absolute;
width: 20px;
height: 20px;
background-repeat: no-repeat;
background-origin: content-box;
box-sizing: border-box;
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+");
background-position: bottom right;
padding: 0 3px 3px 0;
}
.react-resizable-handle-sw {
bottom: 0;
left: 0;
cursor: sw-resize;
transform: rotate(90deg);
}
.react-resizable-handle-se {
bottom: 0;
right: 0;
cursor: se-resize;
}
.react-resizable-handle-nw {
top: 0;
left: 0;
cursor: nw-resize;
transform: rotate(180deg);
}
.react-resizable-handle-ne {
top: 0;
right: 0;
cursor: ne-resize;
transform: rotate(270deg);
}
.react-resizable-handle-w,
.react-resizable-handle-e {
top: 50%;
margin-top: -10px;
cursor: ew-resize;
}
.react-resizable-handle-w {
left: 0;
transform: rotate(135deg);
}
.react-resizable-handle-e {
right: 0;
transform: rotate(315deg);
}
.react-resizable-handle-n,
.react-resizable-handle-s {
left: 50%;
margin-left: -10px;
cursor: ns-resize;
}
.react-resizable-handle-n {
top: 0;
transform: rotate(225deg);
}
.react-resizable-handle-s {
bottom: 0;
transform: rotate(45deg);
}
.react-grid-layout {
position: relative;
transition: height 200ms ease;
}
.react-grid-item {
transition: all 200ms ease;
transition-property: left, top;
}
.react-grid-item.cssTransforms {
transition-property: transform;
}
.react-grid-item.resizing {
z-index: 1;
will-change: width, height;
}
.react-grid-item.react-draggable-dragging {
transition: none;
z-index: 3;
will-change: transform;
}
.react-grid-item.dropping {
visibility: hidden;
}
.react-grid-item.react-grid-placeholder {
background: red;
opacity: 0.2;
transition-duration: 100ms;
z-index: 2;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.react-grid-item > .react-resizable-handle {
position: absolute;
width: 20px;
height: 20px;
bottom: 0;
right: 0;
cursor: se-resize;
}
.react-grid-item > .react-resizable-handle::after {
content: "";
position: absolute;
right: 3px;
bottom: 3px;
width: 5px;
height: 5px;
border-right: 2px solid rgba(0, 0, 0, 0.4);
border-bottom: 2px solid rgba(0, 0, 0, 0.4);
}
.react-resizable-hide > .react-resizable-handle {
display: none;
}

View File

@@ -1,154 +1,12 @@
.react-resizable {
position: relative;
}
.react-resizable-handle {
position: absolute;
width: 20px;
height: 20px;
background-repeat: no-repeat;
background-origin: content-box;
box-sizing: border-box;
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+");
background-position: bottom right;
padding: 0 3px 3px 0;
}
.react-resizable-handle-sw {
bottom: 0;
left: 0;
cursor: sw-resize;
transform: rotate(90deg);
}
.react-resizable-handle-se {
bottom: 0;
right: 0;
cursor: se-resize;
}
.react-resizable-handle-nw {
top: 0;
left: 0;
cursor: nw-resize;
transform: rotate(180deg);
}
.react-resizable-handle-ne {
top: 0;
right: 0;
cursor: ne-resize;
transform: rotate(270deg);
}
.react-resizable-handle-w,
.react-resizable-handle-e {
top: 50%;
margin-top: -10px;
cursor: ew-resize;
}
.react-resizable-handle-w {
left: 0;
transform: rotate(135deg);
}
.react-resizable-handle-e {
right: 0;
transform: rotate(315deg);
}
.react-resizable-handle-n,
.react-resizable-handle-s {
left: 50%;
margin-left: -10px;
cursor: ns-resize;
}
.react-resizable-handle-n {
top: 0;
transform: rotate(225deg);
}
.react-resizable-handle-s {
bottom: 0;
transform: rotate(45deg);
}
.react-grid-layout {
position: relative;
transition: height 200ms ease;
}
.react-grid-item {
transition: all 200ms ease;
transition-property: left, top;
}
.react-grid-item.cssTransforms {
transition-property: transform;
}
.react-grid-item.resizing {
z-index: 1;
will-change: width, height;
}
.react-grid-item.react-draggable-dragging {
transition: none;
z-index: 3;
will-change: transform;
}
.react-grid-item.dropping {
visibility: hidden;
}
.react-grid-item.react-grid-placeholder {
background: red;
opacity: 0.2;
transition-duration: 100ms;
z-index: 2;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.react-grid-item > .react-resizable-handle {
position: absolute;
width: 20px;
height: 20px;
bottom: 0;
right: 0;
cursor: se-resize;
}
.react-grid-item > .react-resizable-handle::after {
content: "";
position: absolute;
right: 3px;
bottom: 3px;
width: 5px;
height: 5px;
border-right: 2px solid rgba(0, 0, 0, 0.4);
border-bottom: 2px solid rgba(0, 0, 0, 0.4);
}
.react-resizable-hide > .react-resizable-handle {
display: none;
}
.dashboard-card {
height: 100%;
width: 100%;
// background-color: green;
.ant-card-body {
height: 80%;
width: 100%;
// // background-color: red;
// height: 90%;
// width: 100%;
// padding: 8px;
// display: flex;
// flex-direction: column;
// align-items: center;
// justify-content: center;
}
.ant-spin-nested-loading {
// background-color: red;
height: 100%;
.ant-spin-container {
height: 100%;
.ant-table {
height: 100%;
.ant-table-container {
height: 100%;
}
}
}
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
}

View File

@@ -1,3 +0,0 @@
export function GenerateDashboardData(data) {
return data;
}

View File

@@ -1,138 +0,0 @@
import { Button, Card, Table, Typography } from "antd";
import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import Dinero from "dinero.js";
import { SyncOutlined } from "@ant-design/icons";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(DmsAllocationsSummary);
export function DmsAllocationsSummary({ socket, bodyshop, jobId, title }) {
const { t } = useTranslation();
const [allocationsSummary, setAllocationsSummary] = useState([]);
useEffect(() => {
if (socket.connected) {
socket.emit("cdk-calculate-allocations", jobId, (ack) => {
setAllocationsSummary(ack);
socket.allocationsSummary = ack;
});
}
}, [socket, socket.connected, jobId]);
const columns = [
{
title: t("jobs.fields.dms.center"),
dataIndex: "center",
key: "center",
},
{
title: t("jobs.fields.dms.sale"),
dataIndex: "sale",
key: "sale",
render: (text, record) => Dinero(record.sale).toFormat(),
},
{
title: t("jobs.fields.dms.cost"),
dataIndex: "cost",
key: "cost",
render: (text, record) => Dinero(record.cost).toFormat(),
},
{
title: t("jobs.fields.dms.sale_dms_acctnumber"),
dataIndex: "sale_dms_acctnumber",
key: "sale_dms_acctnumber",
render: (text, record) =>
record.profitCenter && record.profitCenter.dms_acctnumber,
},
{
title: t("jobs.fields.dms.cost_dms_acctnumber"),
dataIndex: "cost_dms_acctnumber",
key: "cost_dms_acctnumber",
render: (text, record) =>
record.costCenter && record.costCenter.dms_acctnumber,
},
{
title: t("jobs.fields.dms.dms_wip_acctnumber"),
dataIndex: "dms_wip_acctnumber",
key: "dms_wip_acctnumber",
render: (text, record) =>
record.costCenter && record.costCenter.dms_wip_acctnumber,
},
];
return (
<Card
title={title}
extra={
<Button
onClick={() => {
socket.emit("cdk-calculate-allocations", jobId, (ack) =>
setAllocationsSummary(ack)
);
}}
>
<SyncOutlined />
</Button>
}
>
<Table
pagination={{ position: "top", defaultPageSize: 50 }}
columns={columns}
rowKey="center"
dataSource={allocationsSummary}
locale={{ emptyText: t("dms.labels.refreshallocations") }}
summary={() => {
const totals =
allocationsSummary &&
allocationsSummary.reduce(
(acc, val) => {
return {
totalSale: acc.totalSale.add(Dinero(val.sale)),
totalCost: acc.totalCost.add(Dinero(val.cost)),
};
},
{
totalSale: Dinero(),
totalCost: Dinero(),
}
);
return (
<Table.Summary.Row>
<Table.Summary.Cell>
<Typography.Title level={4}>
{t("general.labels.totals")}
</Typography.Title>
</Table.Summary.Cell>
<Table.Summary.Cell>
{totals && totals.totalSale.toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell>
{
// totals.totalCost.toFormat()
}
</Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
</Table.Summary.Row>
);
}}
/>
</Card>
);
}

View File

@@ -1,105 +0,0 @@
import { useLazyQuery } from "@apollo/client";
import { Button, Input, Modal, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { SEARCH_DMS_VEHICLES } from "../../graphql/dms.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkVehicles);
export function DmsCdkVehicles({ bodyshop, form, socket, job }) {
const [visible, setVisible] = useState(false);
const [selectedModel, setSelectedModel] = useState(null);
const { t } = useTranslation();
const [callSearch, { loading, error, data }] =
useLazyQuery(SEARCH_DMS_VEHICLES);
const columns = [
{
title: t("vehicles.fields.v_make_desc"),
dataIndex: "make",
key: "make",
},
{
title: t("vehicles.fields.v_model_desc"),
dataIndex: "model",
key: "model",
},
{
title: t("jobs.fields.dms.dms_make"),
dataIndex: "makecode",
key: "makecode",
},
{
title: t("jobs.fields.dms.dms_model"),
dataIndex: "modelcode",
key: "modelcode",
},
];
return (
<>
<Modal
width={"90%"}
visible={visible}
onCancel={() => setVisible(false)}
onOk={() => {
form.setFieldsValue({
dms_make: selectedModel.makecode,
dms_model: selectedModel.modelcode,
});
setVisible(false);
}}
>
{error && <AlertComponent error={error.message} />}
<Table
title={() => (
<Input.Search
onSearch={(val) => callSearch({ variables: { search: val } })}
placeholder={t("general.labels.search")}
/>
)}
columns={columns}
loading={loading}
rowKey="id"
dataSource={data ? data.search_dms_vehicles : []}
onRow={(record) => {
return {
onClick: () => setSelectedModel(record),
};
}}
rowSelection={{
onSelect: (record) => {
setSelectedModel(record);
},
type: "radio",
selectedRowKeys: [selectedModel && selectedModel.id],
}}
/>
</Modal>
<Button
onClick={() => {
setVisible(true);
callSearch({
variables: {
search: job && job.v_model_desc && job.v_model_desc.substr(0, 3),
},
});
}}
>
{t("jobs.actions.dms.findmakemodelcode")}
</Button>
</>
);
}

View File

@@ -1,40 +0,0 @@
import { Button } from "antd";
import axios from "axios";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkMakesRefetch);
export function DmsCdkMakesRefetch({ currentUser, bodyshop, form, socket }) {
const [loading, setLoading] = useState(false);
const { t } = useTranslation();
if (!currentUser.email.includes("@imex.")) return null;
const handleRefetch = async () => {
setLoading(true);
await axios.post("/cdk/getvehicles", {
cdk_dealerid: bodyshop.cdk_dealerid,
bodyshopid: bodyshop.id,
});
setLoading(false);
};
return (
<Button loading={loading} onClick={handleRefetch}>
{t("jobs.actions.dms.refetchmakesmodels")}
</Button>
);
}

View File

@@ -1,161 +0,0 @@
import { Button, Table, Col, Checkbox } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { socket } from "../../pages/dms/dms.container";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { alphaSort } from "../../utils/sorters";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(DmsCustomerSelector);
export function DmsCustomerSelector({ bodyshop }) {
const { t } = useTranslation();
const [customerList, setcustomerList] = useState([]);
const [visible, setVisible] = useState(false);
const [selectedCustomer, setSelectedCustomer] = useState(null);
const [dmsType, setDmsType] = useState("cdk");
socket.on("cdk-select-customer", (customerList, callback) => {
setVisible(true);
setDmsType("cdk");
setcustomerList(customerList);
});
socket.on("pbs-select-customer", (customerList, callback) => {
setVisible(true);
setDmsType("pbs");
setcustomerList(customerList);
});
const onUseSelected = () => {
setVisible(false);
socket.emit(`${dmsType}-selected-customer`, selectedCustomer);
setSelectedCustomer(null);
};
const onUseGeneric = () => {
setVisible(false);
socket.emit(
`${dmsType}selected-customer`,
bodyshop.cdk_configuration.generic_customer_number
);
setSelectedCustomer(null);
};
const onCreateNew = () => {
setVisible(false);
socket.emit(`${dmsType}-selected-customer`, null);
setSelectedCustomer(null);
};
const cdkColumns = [
{
title: t("jobs.fields.dms.id"),
dataIndex: ["id", "value"],
key: "id",
},
{
title: t("jobs.fields.dms.vinowner"),
dataIndex: "vinOwner",
key: "vinOwner",
render: (text, record) => <Checkbox disabled checked={record.vinOwner} />,
},
{
title: t("jobs.fields.dms.name1"),
dataIndex: ["name1", "fullName"],
key: "name1",
sorter: (a, b) =>
alphaSort(a.name1 && a.name1.fullName, b.name1 && b.name1.fullName),
},
{
title: t("jobs.fields.dms.address"),
//dataIndex: ["name2", "fullName"],
key: "address",
render: (record, value) =>
`${
record.address &&
record.address.addressLine &&
record.address.addressLine[0]
}, ${record.address && record.address.city} ${
record.address && record.address.stateOrProvince
} ${record.address && record.address.postalCode}`,
},
];
const pbsColumns = [
{
title: t("jobs.fields.dms.id"),
dataIndex: "ContactId",
key: "ContactId",
},
{
title: t("jobs.fields.dms.name1"),
key: "name1",
sorter: (a, b) => alphaSort(a.LastName, b.LastName),
render: (text, record) =>
`${record.FirstName || ""} ${record.LastName || ""}`,
},
{
title: t("jobs.fields.dms.address"),
key: "address",
render: (record, value) =>
`${record.Address}, ${record.City} ${record.State} ${record.ZipCode}`,
},
];
if (!visible) return null;
return (
<Col span={24}>
<Table
title={() => (
<div>
<Button onClick={onUseSelected} disabled={!selectedCustomer}>
{t("jobs.actions.dms.useselected")}
</Button>
<Button
onClick={onUseGeneric}
disabled={
!(
bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.generic_customer_number
)
}
>
{t("jobs.actions.dms.usegeneric")}
</Button>
<Button onClick={onCreateNew}>
{t("jobs.actions.dms.createnewcustomer")}
</Button>
</div>
)}
pagination={{ position: "top" }}
columns={dmsType === "cdk" ? cdkColumns : pbsColumns}
rowKey={(record) =>
dmsType === "cdk" ? record.id.value : record.ContactId
}
dataSource={customerList}
//onChange={handleTableChange}
rowSelection={{
onSelect: (record) => {
setSelectedCustomer(
dmsType === "cdk" ? record.id.value : record.ContactId
);
},
type: "radio",
selectedRowKeys: [selectedCustomer],
}}
/>
</Col>
);
}

View File

@@ -1,57 +0,0 @@
import { Divider, Space, Tag, Timeline } from "antd";
import moment from "moment";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
setBreadcrumbs,
setSelectedHeader,
} from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsLogEvents);
export function DmsLogEvents({ socket, logs, bodyshop }) {
return (
<Timeline pending
reverse={true}
>
{logs.map((log, idx) => (
<Timeline.Item key={idx} color={LogLevelHierarchy(log.level)}>
<Space wrap align="start" style={{}}>
<Tag color={LogLevelHierarchy(log.level)}>{log.level}</Tag>
<span>{moment(log.timestamp).format("MM/DD/YYYY HH:mm:ss")}</span>
<Divider type="vertical" />
<span>{log.message}</span>
</Space>
</Timeline.Item>
))}
</Timeline>
);
}
function LogLevelHierarchy(level) {
switch (level) {
case "TRACE":
return "pink";
case "DEBUG":
return "orange";
case "INFO":
return "blue";
case "WARNING":
return "yellow";
case "ERROR":
return "red";
default:
return 0;
}
}

View File

@@ -1,395 +0,0 @@
import { DeleteFilled, DownOutlined } from "@ant-design/icons";
import {
Button,
Card,
Divider,
Dropdown,
Form,
Input,
InputNumber,
Menu,
Select,
Space,
Statistic,
Typography,
} from "antd";
import Dinero from "dinero.js";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { determineDmsType } from "../../pages/dms/dms.container";
import { selectBodyshop } from "../../redux/user/user.selectors";
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsPostForm);
export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
const [form] = Form.useForm();
const { t } = useTranslation();
const handlePayerSelect = (value, index) => {
form.setFieldsValue({
payers: form.getFieldValue("payers").map((payer, mapIndex) => {
if (index !== mapIndex) return payer;
const cdkPayer =
bodyshop.cdk_configuration.payers &&
bodyshop.cdk_configuration.payers.find((i) => i.name === value);
if (!cdkPayer) return payer;
return {
...cdkPayer,
dms_acctnumber: cdkPayer.dms_acctnumber,
controlnumber: job && job[cdkPayer.control_type],
};
}),
});
};
const handleFinish = (values) => {
socket.emit(`${determineDmsType(bodyshop)}-export-job`, {
jobid: job.id,
txEnvelope: values,
});
console.log(logsRef);
if (logsRef) {
console.log("executing", logsRef);
logsRef.curent &&
logsRef.current.scrollIntoView({
behavior: "smooth",
});
}
};
return (
<Card title={t("jobs.labels.dms.postingform")}>
<Form
form={form}
layout="vertical"
onFinish={handleFinish}
initialValues={{
story: t("jobs.labels.dms.defaultstory", {
ro_number: job.ro_number,
area_of_damage: job.area_of_damage && job.area_of_damage.impact1,
}).substr(0, 239),
}}
>
<LayoutFormRow grow>
<Form.Item
name="journal"
label={t("jobs.fields.dms.journal")}
initialValue={
bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.default_journal
}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="kmin"
label={t("jobs.fields.kmin")}
initialValue={job && job.kmin}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber disabled />
</Form.Item>
<Form.Item
name="kmout"
label={t("jobs.fields.kmout")}
initialValue={job && job.kmout}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber disabled />
</Form.Item>
</LayoutFormRow>
{bodyshop.cdk_dealerid && (
<div>
<LayoutFormRow style={{ justifyContent: "center" }} grow>
<Form.Item
name="dms_make"
label={t("jobs.fields.dms.dms_make")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<Form.Item
name="dms_model"
label={t("jobs.fields.dms.dms_model")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
</LayoutFormRow>
<Space>
<DmsCdkMakes form={form} socket={socket} job={job} />
<DmsCdkMakesRefetch />
</Space>
</div>
)}
<Form.Item
name="story"
label={t("jobs.fields.dms.story")}
rules={[
{
required: true,
},
]}
>
<Input.TextArea maxLength={240} />
</Form.Item>
<Divider />
<Form.List name={["payers"]}>
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Space wrap>
<Form.Item
label={t("jobs.fields.dms.payer.name")}
key={`${index}name`}
name={[field.name, "name"]}
rules={[
{
required: true,
},
]}
>
<Select
style={{ minWidth: "15rem" }}
onSelect={(value) => handlePayerSelect(value, index)}
>
{bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.payers &&
bodyshop.cdk_configuration.payers.map((payer) => (
<Select.Option key={payer.name}>
{payer.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("jobs.fields.dms.payer.dms_acctnumber")}
key={`${index}dms_acctnumber`}
name={[field.name, "dms_acctnumber"]}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<Form.Item
label={t("jobs.fields.dms.payer.amount")}
key={`${index}amount`}
name={[field.name, "amount"]}
rules={[
{
required: true,
},
]}
>
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={
<div>
{t("jobs.fields.dms.payer.controlnumber")}{" "}
<Dropdown
overlay={
<Menu>
{bodyshop.cdk_configuration.controllist &&
bodyshop.cdk_configuration.controllist.map(
(key, idx) => (
<Menu.Item
key={idx}
onClick={() => {
form.setFieldsValue({
payers: form
.getFieldValue("payers")
.map((row, mapIndex) => {
if (index !== mapIndex)
return row;
return {
...row,
controlnumber:
key.controlnumber,
};
}),
});
}}
>
{key.name}
</Menu.Item>
)
)}
</Menu>
}
>
<a href=" #" onClick={(e) => e.preventDefault()}>
<DownOutlined />
</a>
</Dropdown>
</div>
}
key={`${index}controlnumber`}
name={[field.name, "controlnumber"]}
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
const payers = form.getFieldValue("payers");
const row = payers && payers[index];
const cdkPayer =
bodyshop.cdk_configuration.payers &&
bodyshop.cdk_configuration.payers.find(
(i) => i && row && i.name === row.name
);
return (
<div>
{cdkPayer &&
t(`jobs.fields.${cdkPayer.control_type}`)}
</div>
);
}}
</Form.Item>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
</Space>
</Form.Item>
))}
<Form.Item>
<Button
disabled={!(fields.length < 3)}
onClick={() => {
if (fields.length < 3) add();
}}
style={{ width: "100%" }}
>
{t("jobs.actions.dms.addpayer")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item shouldUpdate>
{() => {
//Perform Calculation to determine discrepancy.
let totalAllocated = Dinero();
const payers = form.getFieldValue("payers");
payers &&
payers.forEach((payer) => {
totalAllocated = totalAllocated.add(
Dinero({ amount: Math.round((payer?.amount || 0) * 100) })
);
});
const totals =
socket.allocationsSummary &&
socket.allocationsSummary.reduce(
(acc, val) => {
return {
totalSale: acc.totalSale.add(Dinero(val.sale)),
totalCost: acc.totalCost.add(Dinero(val.cost)),
};
},
{
totalSale: Dinero(),
totalCost: Dinero(),
}
);
const discrep = totals
? totals.totalSale.subtract(totalAllocated)
: Dinero();
return (
<Space size="large" wrap align="center">
<Statistic
title={t("jobs.labels.subtotal")}
value={(totals ? totals.totalSale : Dinero()).toFormat()}
/>
<Typography.Title>-</Typography.Title>
<Statistic
title={t("jobs.labels.dms.totalallocated")}
value={totalAllocated.toFormat()}
/>
<Typography.Title>=</Typography.Title>
<Statistic
title={t("jobs.labels.dms.notallocated")}
valueStyle={{
color: discrep.getAmount() === 0 ? "green" : "red",
}}
value={discrep.toFormat()}
/>
<Button
disabled={
!socket.allocationsSummary || discrep.getAmount() !== 0
}
htmlType="submit"
>
{t("jobs.actions.dms.post")}
</Button>
</Space>
);
}}
</Form.Item>
</Form>
</Card>
);
}

View File

@@ -1,111 +0,0 @@
//import "tui-image-editor/dist/tui-image-editor.css";
import { Result } from "antd";
import * as markerjs2 from "markerjs2";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { handleUpload } from "../documents-upload/documents-upload.utility";
import { GenerateSrcUrl } from "../jobs-documents-gallery/job-documents.utility";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function DocumentEditorComponent({ currentUser, bodyshop, document }) {
const imgRef = useRef(null);
const [loading, setLoading] = useState(false);
const [uploaded, setuploaded] = useState(false);
const markerArea = useRef(null);
const { t } = useTranslation();
const triggerUpload = useCallback(
async (dataUrl) => {
setLoading(true);
handleUpload(
{
filename: `${document.key.split("/").pop()}-${Date.now()}.jpg`,
file: await b64toBlob(dataUrl),
onSuccess: () => {
setLoading(false);
setuploaded(true);
},
onError: () => setLoading(false),
},
{
bodyshop: bodyshop,
uploaded_by: currentUser.email,
jobId: document.jobid,
//billId: billId,
tagsArray: ["edited"],
//callback: callbackAfterUpload,
}
);
},
[bodyshop, currentUser, document]
);
useEffect(() => {
if (imgRef.current !== null) {
// create a marker.js MarkerArea
markerArea.current = new markerjs2.MarkerArea(imgRef.current);
// attach an event handler to assign annotated image back to our image element
markerArea.current.addCloseEventListener((closeEvent) => {});
markerArea.current.addRenderEventListener((dataUrl) => {
imgRef.current.src = dataUrl;
markerArea.current.close();
triggerUpload(dataUrl);
});
// launch marker.js
markerArea.current.renderAtNaturalSize = true;
markerArea.current.renderImageType = "image/jpeg";
markerArea.current.renderImageQuality = 1;
//markerArea.current.settings.displayMode = "inline";
markerArea.current.show();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [triggerUpload]);
async function b64toBlob(url) {
const res = await fetch(url);
return await res.blob();
}
return (
<div>
{!loading && !uploaded && (
<img
ref={imgRef}
src={GenerateSrcUrl(document)}
alt="sample"
crossOrigin="anonymous"
style={{ maxWidth: "90vw", maxHeight: "90vh" }}
/>
)}
{loading && <LoadingSpinner message={t("documents.labels.uploading")} />}
{uploaded && (
<Result
status="success"
title={t("documents.successes.edituploaded")}
/>
)}
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(DocumentEditorComponent);

View File

@@ -1,59 +0,0 @@
import { useQuery } from "@apollo/client";
import { Result } from "antd";
import queryString from "query-string";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useLocation } from "react-router";
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
import { GET_DOCUMENT_BY_PK } from "../../graphql/documents.queries";
import { setBodyshop } from "../../redux/user/user.actions";
import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import DocumentEditor from "./document-editor.component";
const mapDispatchToProps = (dispatch) => ({
setBodyshop: (bs) => dispatch(setBodyshop(bs)),
});
export default connect(null, mapDispatchToProps)(DocumentEditorContainer);
export function DocumentEditorContainer({ setBodyshop }) {
//Get the image details for the image to be saved.
//Get the document id from the search string.
const { documentId } = queryString.parse(useLocation().search);
const { t } = useTranslation();
const {
loading: loadingShop,
error: errorShop,
data: dataShop,
} = useQuery(QUERY_BODYSHOP, {
fetchPolicy: "network-only",
});
useEffect(() => {
if (dataShop) setBodyshop(dataShop.bodyshops[0]);
}, [dataShop, setBodyshop]);
const { loading, error, data } = useQuery(GET_DOCUMENT_BY_PK, {
variables: { documentId },
skip: !documentId,
});
if (loading || loadingShop) return <LoadingSpinner />;
if (error || errorShop)
return (
<AlertComponent
message={error.message || errorShop.message}
type="error"
/>
);
if (!data || !data.documents_by_pk)
return <Result status="404" title={t("general.errors.notfound")} />;
return (
<div>
<DocumentEditor document={data ? data.documents_by_pk : null} />
</div>
);
}

View File

@@ -5,8 +5,6 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_NEW_DOCUMENT } from "../../graphql/documents.queries";
import { axiosAuthInterceptorId } from "../../utils/CleanAxios";
import client from "../../utils/GraphQLClient";
import exifr from "exifr";
//Context: currentUserEmail, bodyshop, jobid, invoiceid
//Required to prevent headers from getting set and rejected from Cloudinary.
@@ -14,18 +12,15 @@ var cleanAxios = axios.create();
cleanAxios.interceptors.request.eject(axiosAuthInterceptorId);
export const handleUpload = (ev, context) => {
console.log("Handling Upload", ev);
logImEXEvent("document_upload", { filetype: ev.file.type });
const { onError, onSuccess, onProgress } = ev;
const { bodyshop, jobId } = context;
const fileName = ev.file.name || ev.filename;
let key = `${bodyshop.id}/${jobId}/${fileName.replace(
/\.[^/.]+$/,
""
)}-${new Date().getTime()}`;
let extension = fileName.split(".").pop();
let key = `${bodyshop.id}/${jobId}/${ev.file.name.replace(/\.[^/.]+$/, "")}`;
let extension = ev.file.name.split(".").pop();
uploadToCloudinary(
key,
extension,
@@ -59,7 +54,7 @@ export const uploadToCloudinary = async (
// let eager = process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS;
//Get the signed url.
console.log("fileType", fileType);
const upload_preset = fileType.startsWith("video")
? "incoming_upload_video"
: "incoming_upload";
@@ -72,6 +67,7 @@ export const uploadToCloudinary = async (
});
if (signedURLResponse.status !== 200) {
console.log("Error Getting Signed URL", signedURLResponse.statusText);
if (!!onError) onError(signedURLResponse.statusText);
notification["error"]({
message: i18n.t("documents.errors.getpresignurl", {
@@ -89,7 +85,6 @@ export const uploadToCloudinary = async (
if (!!onProgress) onProgress({ percent: (e.loaded / e.total) * 100 });
},
};
const formData = new FormData();
formData.append("file", file);
@@ -110,8 +105,13 @@ export const uploadToCloudinary = async (
...options,
}
);
console.log("Upload Response", cloudinaryUploadResponse.data);
if (cloudinaryUploadResponse.status !== 200) {
console.log(
"Error uploading to cloudinary.",
cloudinaryUploadResponse.statusText
);
if (!!onError) onError(cloudinaryUploadResponse.statusText);
notification["error"]({
message: i18n.t("documents.errors.insert", {
@@ -122,16 +122,6 @@ export const uploadToCloudinary = async (
}
//Insert the document with the matching key.
let takenat;
if (fileType.includes("image")) {
try {
const exif = await exifr.parse(file);
takenat = exif && exif.DateTimeOriginal;
} catch (error) {
console.log("Unable to parse image file for EXIF Data");
}
}
const documentInsert = await client.mutate({
mutation: INSERT_NEW_DOCUMENT,
variables: {
@@ -142,10 +132,9 @@ export const uploadToCloudinary = async (
uploaded_by: uploaded_by,
key: key,
type: fileType,
extension: cloudinaryUploadResponse.data.format || extension,
extension: extension,
bodyshopid: bodyshop.id,
size: cloudinaryUploadResponse.data.bytes || file.size,
takenat,
},
],
},
@@ -177,7 +166,6 @@ export const uploadToCloudinary = async (
}
};
//Also needs to be updated in media JS and mobile app.
export function DetermineFileType(filetype) {
if (!filetype) return "auto";
else if (filetype.startsWith("image")) return "image";

Some files were not shown because too many files have changed in this diff Show More