Wednesday, April 10, 2013

Mark Invoice Paid With One Click

Lots of customers don't have the need for multiple closed status reasons on an invoice. It is either paid in full or not. But they still like to use the button in the ribbon to mark the invoice "paid" with a "complete" status. A quick solution so the user doesn't have to make that extra click to choose "complete" from the resulting dialog is to hide the exiting "Invoice Paid" button and replace it with a custom button with a little JavaScript behind it.

Create a new JavaScript web resource file - we'll call it "new_invoice_ribbon" - it sound contain the following:

function MarkInvoicePaid() {
    if (Xrm.Page.data.entity.getIsDirty()) {
        alert("Please save your changes first");
        return;
    }

    var req = ""
    req += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
    req += "<s:Body>";
    req += "<Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
    req += "<request xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\">";
    req += "<a:Parameters xmlns:b=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
    req += "<a:KeyValuePairOfstringanyType>";
    req += "<b:key>EntityMoniker</b:key>";
    req += "<b:value i:type=\"a:EntityReference\">";
    req += "<a:Id>" + Xrm.Page.data.entity.getId() + "</a:Id>";
    req += "<a:LogicalName>invoice</a:LogicalName>";
    req += "<a:Name i:nil=\"true\" />";
    req += "</b:value>";
    req += "</a:KeyValuePairOfstringanyType>";
    req += "<a:KeyValuePairOfstringanyType>";
    req += "<b:key>State</b:key>";
    req += "<b:value i:type=\"a:OptionSetValue\">";
    req += "<a:Value>2</a:Value>";
    req += "</b:value>";
    req += "</a:KeyValuePairOfstringanyType>";
    req += "<a:KeyValuePairOfstringanyType>";
    req += "<b:key>Status</b:key>";
    req += "<b:value i:type=\"a:OptionSetValue\">";
    req += "<a:Value>100001</a:Value>";
    req += "</b:value>";
    req += "</a:KeyValuePairOfstringanyType>";
    req += "</a:Parameters>";
    req += "<a:RequestId i:nil=\"true\" />";
    req += "<a:RequestName>SetState</a:RequestName>";
    req += "</request>";
    req += "</Execute>";
    req += "</s:Body>";
    req += "</s:Envelope>";

    var serverUrl = Xrm.Page.context.getClientUrl() + 
        "/XRMServices/2011/Organization.svc/web";
    var req = new XMLHttpRequest();
    req.open("POST", serverUrl, true)
    req.setRequestHeader("Accept", "application/xml, text/xml, */*");
    req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    req.setRequestHeader("SOAPAction", 
        "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
    req.onreadystatechange = function () {
        assignResponse(req);
    };
    req.send(req);
}

function assignResponse(req) {
    if (req.readyState == 4) {
        if (req.status == 200) {
            window.location.reload(true);
        }
        else {
            alert("Error Marking Invoive Paid - " + req.responseXML);
        }
    }
}

Then you have a choice, you can export the invoice entity and directly edit the RibbonDiffXml or you can use a tool like Ribbon Workbench and hide the existing button and create new one.

For those who would rather just paste in the XML:

<RibbonDiffXml>
    <CustomActions>
        <CustomAction Id="new.invoice.Button5.Button.CustomAction" Location="Mscrm.Form.invoice.MainTab.Actions.Controls._children" Sequence="1">
            <CommandUIDefinition>
                <Button Alt="$LocLabels:new.invoice.Button5.Button.Alt" Command="new.invoice.Command3.Command" Id="new.invoice.Button5.Button" Image32by32="/_imgs/ribbon/InvoicePaid_32.png" Image16by16="/_imgs/ribbon/InvoicePaid_16.png" LabelText="$LocLabels:new.invoice.Button5.Button.LabelText" Sequence="1" TemplateAlias="o1" ToolTipTitle="$LocLabels:new.invoice.Button5.Button.ToolTipTitle" ToolTipDescription="$LocLabels:new.invoice.Button5.Button.ToolTipDescription" />
            </CommandUIDefinition>
        </CustomAction>
        <HideCustomAction HideActionId="new.Mscrm.Form.invoice.InvoicePaid.Hide" Location="Mscrm.Form.invoice.InvoicePaid" />
    </CustomActions>
    <Templates>
        <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
    </Templates>
    <CommandDefinitions>
        <CommandDefinition Id="new.invoice.Command3.Command">
            <EnableRules>
                <EnableRule Id="new.invoice.EnableRule0.EnableRule" />
            </EnableRules>
            <DisplayRules>
                <DisplayRule Id="new.invoice.DisplayRule0.DisplayRule" />
            </DisplayRules>
            <Actions>
                <JavaScriptFunction FunctionName="MarkInvoicePaid" Library="$webresource:new_invoice_ribbon" />
            </Actions>
        </CommandDefinition>
    </CommandDefinitions>
    <RuleDefinitions>
        <TabDisplayRules />
        <DisplayRules>
            <DisplayRule Id="new.invoice.DisplayRule0.DisplayRule">
                <FormStateRule State="Create" Default="false" InvertResult="true" />
            </DisplayRule>
        </DisplayRules>
        <EnableRules>
            <EnableRule Id="new.invoice.EnableRule0.EnableRule">
                <FormStateRule State="Disabled" Default="false" InvertResult="true" />
            </EnableRule>
        </EnableRules>
    </RuleDefinitions>
    <LocLabels>
        <LocLabel Id="new.invoice.Button5.Button.Alt">
            <Titles>
                <Title description="Invoice Paid" languagecode="1033" />
            </Titles>
        </LocLabel>
        <LocLabel Id="new.invoice.Button5.Button.LabelText">
            <Titles>
                <Title description="Invoice Paid" languagecode="1033" />
            </Titles>
        </LocLabel>
        <LocLabel Id="new.invoice.Button5.Button.ToolTipDescription">
            <Titles>
                <Title description="Mark the invoice as completely paid." languagecode="1033" />
            </Titles>
        </LocLabel>
        <LocLabel Id="new.invoice.Button5.Button.ToolTipTitle">
            <Titles>
                <Title description="Invoice Paid" languagecode="1033" />
            </Titles>
        </LocLabel>
    </LocLabels>
</RibbonDiffXml>

Once you get everything published your "Invoice Paid" button should close the invoice as "paid" and "complete" with a single click.