<field reporter:label="Note" name="note" reporter:datatype="text" />
<field reporter:label="Collection Code" name="collection_code" reporter:datatype="text" />
<field reporter:label="Cancel Reason" name="cancel_reason" reporter:datatype="link" />
+ <field reporter:label="Claims" name="claims" reporter:datatype="link" oils_persist:virtual="true" />
</fields>
<links>
<link field="lineitem" reltype="has_a" key="id" map="" class="jub"/>
<link field="location" reltype="has_a" key="id" map="" class="acpl"/>
<link field="circ_modifier" reltype="has_a" key="code" map="" class="ccm"/>
<link field="cancel_reason" reltype="has_a" key="id" map="" class="acqcr"/>
+ <link field="claims" reltype="has_many" key="lineitem_detail" map="" class="acqcl"/>
</links>
</class>
created. This is an array of acqclpa IDs./,
type => "array"},
],
- return => {desc => "The claim events on success, Event on error",
- type => "object", class => "acrlid"}
+ return => {
+ desc => "The claim voucher events on success, Event on error",
+ type => "object", class => "acrlid"
+ }
}
);
type => 'array'
},
],
- return => {desc => 'The claim events on success, Event on error', type => 'object', class => 'acrlid'}
+ return => {
+ desc => "The claim voucher events on success, Event on error",
+ type => "object", class => "acrlid"
+ }
}
);
}
if($self->api_name =~ /claim.lineitem_detail/) {
-
- my $lid = $e->retrieve_acq_lineitem_detail([$object_id, $lid_flesh]) or
- return $e->die_event;
- return $evt if
- $evt = claim_lineitem_detail(
- $e, $lid, $claim, $claim_type, $policy_actions, $note, $claim_events);
+ my $lids = $e->search_acq_lineitem_detail([
+ {"id" => $object_id, "cancel_reason" => undef},
+ $lid_flesh
+ ]) or return $e->die_event;
+ foreach my $lid (@$lids) {
+ return $evt if
+ $evt = claim_lineitem_detail(
+ $e, $lid, $claim, $claim_type, $policy_actions,
+ $note, $claim_events
+ );
+ }
} elsif($self->api_name =~ /claim.lineitem/) {
my $lids = $e->search_acq_lineitem_detail([
$li->item_count(scalar(@$details));
}
+ # attach claims to LIDs
+ if($$options{flesh_li_details}) {
+ foreach (@{$li->lineitem_details}) {
+ $_->claims(
+ $e->search_acq_claim([
+ {"lineitem_detail", $_->id}, {
+ "flesh" => 1, "flesh_fields" => {"acqcl" => ["type"]}
+ }
+ ])
+ );
+ }
+ }
+
return $e->event unless (
$li->purchase_order and
$e->allowed(['VIEW_PURCHASE_ORDER', 'CREATE_PURCHASE_ORDER'],
.acq-lit-table-spacer { height:20px; }
.acq-lit-row td[name="selector"] { width:1.5em; font-weight:bold; color:blue; font-size:110%;}
span[name="worksheet"] { padding: 0 6px; }
+#acq-lit-li-claim-dia-lid-list-init { margin-left: 8px; }
#acq-worksheet-contents thead th { font-weight: bold; background-color: #ccc; text-align: center; border-bottom: 1px #000 solid; border-right: 1px #000 solid; padding: 0 6px; }
#acq-worksheet-contents tbody td { text-align: left; vertical-align: top; border: 1px #999 inset; padding: 0 2px; }
'UNNAMED': "Unnamed",
'NO_FIND_INVOICE': "Could not find that invoice.\nNote that the Invoice # field is case-sensitive.",
'NO_LI_TO_CLAIM': "You have not selected any lineitems to claim.",
- 'CLAIM_VOUCHERS': "Claim Vouchers",
- 'PRINT': "Print",
- 'ATTACH_CLAIM_POLICY': "Attach claim policy",
- 'CHANGE_CLAIM_POLICY': "Change claim policy"
+ 'NO_LID_TO_CLAIM': "You have not selected any lineitem details to claim.",
+ 'CHANGE_CLAIM_POLICY': "Change claim policy",
+ 'CANCELED': "Canceled",
+ 'RECVD': "Recv'd",
+ 'NOT_RECVD': "Not recv'd",
+ 'PRINT': "Print"
}
--- /dev/null
+function ClaimDialogManager(dialog, finalDialog, eligibleLidByLi) {
+ var self = this;
+
+ this.anyLids = false;
+ this.anyEligible = false;
+ this.showingLidNodes = {};
+
+ this.dialog = dialog;
+ this.finalDialog = finalDialog;
+ this.eligibleLidByLi = eligibleLidByLi;
+
+ this.showingList = dojo.byId("acq-lit-li-claim-dia-lid-list");
+ this.eligibleList = dojo.byId("acq-lit-li-claim-dia-lid-list-init");
+
+ this.showingLidTemplate = this.showingList.removeChild(
+ nodeByName("lid", this.showingList)
+ );
+ this.showingClaimTemplate =
+ nodeByName("claims", this.showingLidTemplate).removeChild(
+ nodeByName("claim", this.showingLidTemplate)
+ );
+ this.eligibleTemplate = this.eligibleList.removeChild(
+ nodeByName("lid_to_claim", this.eligibleList)
+ );
+
+ dojo.byId("acq-lit-li-claim-dia-claim").onclick = function() {
+ var lid_ids = self.getSelectedEligible();
+ if (lid_ids.length) {
+ dojo.byId("acq-eligible-claim-submit").onclick = function() {
+ self.finalDialog.hide();
+ self.claim(lid_ids);
+ };
+ self.dialog.hide();
+ self.finalDialog.show();
+ }
+ else {
+ alert(localeStrings.NO_LID_TO_CLAIM);
+ }
+ };
+
+ new openils.widget.AutoFieldWidget({
+ "fmClass": "acqclt",
+ "selfReference": true,
+ "dijitArgs": {"required": true},
+ "parentNode": dojo.byId("acq-eligible-claim-type")
+ }).build(function(w) { self.claimType = w; });
+
+ this.reset = function(li) {
+ this.anyLids = false;
+ this.showingLidNodes = {};
+
+ openils.Util.hide("acq-lit-li-claim-dia-initiate");
+ openils.Util.hide("acq-lit-li-claim-dia-show");
+
+ dojo.empty(this.showingList);
+ dojo.empty(this.eligibleList);
+ };
+
+ this.show = function(li) {
+ this.reset();
+ this.prepare(li);
+ this.dialog.show();
+ };
+
+ this.hide = function() { this.dialog.hide(); };
+
+ this.prepare = function(li) {
+ dojo.byId("acq-lit-li-claim-dia-li-title").innerHTML =
+ li.attributes().filter(
+ function(o) { return Boolean(o.attr_name() == "title"); }
+ )[0].attr_value();
+ dojo.byId("acq-lit-li-claim-dia-li-id").innerHTML = li.id();
+
+ li.lineitem_details().forEach(
+ function(lid) {
+ lid.claims().forEach(
+ function(claim) { self.addClaim(lid, claim); }
+ );
+ if (self.eligibleLidByLi[li.id()].indexOf(lid.id()) != -1) {
+ self.addEligible(lid);
+ }
+ }
+ );
+ };
+
+ this._reprReceived = function(lid) {
+ if (lid.cancel_reason())
+ return localeStrings.CANCELED + ": " + lid.cancel_reason().label();
+ else if (lid.recv_time())
+ return localeStrings.RECVD + " " + lid.recv_time();
+ else
+ return localeStrings.NOT_RECVD;
+ };
+
+ this.addClaim = function(lid, claim) {
+ if (!this.anyLids)
+ openils.Util.show("acq-lit-li-claim-dia-show");
+ this.anyLids = true;
+
+ var lidNode = this.showingLidNodes[lid.id()];
+ if (!lidNode) {
+ lidNode = dojo.clone(this.showingLidTemplate);
+ nodeByName("barcode", lidNode).innerHTML = lid.barcode();
+ nodeByName("recvd", lidNode).innerHTML = this._reprReceived(lid);
+
+ this.showingLidNodes[lid.id()] = lidNode;
+ dojo.place(lidNode, this.showingList, "last");
+ }
+
+ var claimNode = dojo.clone(this.showingClaimTemplate);
+ nodeByName("type", claimNode).innerHTML = claim.type().code();
+ nodeByName("voucher", claimNode).onclick = function() {
+ alert("unimplemented"); // XXX
+ // TODO For some reason, the format.acqcle.html events that get
+ // fired by claim_item() in Claims.pm don't stick around in
+ // the database. Must find out why and fix this before writing
+ // the ML method that the following code would call.
+ /*
+ var win;
+ fieldmapper.standardRequest(
+ ["open-ils.acq", "open-ils.acq.claim.get_format_events"], {
+ "params": [openils.User.authtoken, lid.id()],
+ "async": true,
+ "onresponse": function(r) {
+ if (r = openils.Util.readResponse(r)) {
+ if (!win)
+ win = openClaimVoucherWindow();
+ }
+ dojo.byId("main", win.document).innerHTML +=
+ (contents + "<hr />");
+ },
+ "oncomplete": function() {
+ var print_button = dojo.byId("print", win.document);
+ print_button.innerHTML = localeStrings.PRINT;
+ print_button.disabled = false;
+ }
+ }
+ );
+ */
+ };
+
+ dojo.place(
+ claimNode, nodeByName("claims", lidNode), "last"
+ );
+ };
+
+ this.addEligible = function(lid) {
+ if (!this.anyEligible)
+ openils.Util.show("acq-lit-li-claim-dia-initiate");
+ this.anyEligible = true;
+
+ var eligibleNode = dojo.clone(this.eligibleTemplate);
+ nodeByName("claimable_lid", eligibleNode).value = lid.id();
+ dojo.attr(
+ nodeByName("claimable_lid", eligibleNode),
+ "id", "claim-lid-" + lid.id()
+ );
+ dojo.attr(
+ nodeByName("claimable_lid_label", eligibleNode),
+ "for", "claim-lid-" + lid.id()
+ );
+ nodeByName("barcode", eligibleNode).innerHTML = lid.barcode();
+ nodeByName("recvd", eligibleNode).innerHTML = this._reprReceived(lid);
+
+ dojo.place(eligibleNode, this.eligibleList, "last");
+ };
+
+ this.getSelectedEligible = function() {
+ return dojo.query("input[name='claimable_lid']", this.eligibleList).
+ filter(function(o) { return o.checked; }).
+ map(function(o) { return o.value; });
+ };
+
+ this.claim = function(lid_ids) {
+ progressDialog.show(true);
+ var win = null;
+
+ fieldmapper.standardRequest(
+ ["open-ils.acq", "open-ils.acq.claim.lineitem_detail"], {
+ "params": [
+ openils.User.authtoken, lid_ids, null,
+ this.claimType.attr("value"),
+ dijit.byId("acq-eligible-claim-note").attr("value")
+ ],
+ "async": true,
+ "onresponse": function(r) {
+ if (r = openils.Util.readResponse(r)) {
+ if (!win)
+ win = openClaimVoucherWindow();
+ dojo.byId("main", win.document).innerHTML +=
+ (r.template_output().data() + "<hr />");
+ }
+ else {
+ progressDialog.hide();
+ }
+ },
+ "oncomplete": function() {
+ progressDialog.hide();
+ dojo.byId("print", win.document).innerHTML =
+ localeStrings.PRINT;
+ dojo.byId("print", win.document).disabled = false;
+ }
+ }
+ );
+ };
+}
this.virtDfaCounts = {};
this.virtDfaId = -1;
this.dfeOffset = 0;
+ this.claimEligibleLidByLi = {};
this.toggleState = false;
this.tbody = dojo.byId('acq-lit-tbody');
this.selectors = [];
this.realCopiesRow = this.realCopiesTbody.removeChild(dojo.byId('acq-lit-real-copies-row'));
this._copy_fields_for_acqdf = ['owning_lib', 'location'];
this.invoiceLinkDialogManager = new InvoiceLinkDialogManager("li");
+ this.claimDialog = new ClaimDialogManager(
+ liClaimDialog, finalClaimDialog, this.claimEligibleLidByLi
+ );
dojo.connect(acqLitLiActionsSelector, 'onChange',
function() {
dojo.query('[name=copieslink]', row)[0].onclick = function() {self.drawCopies(li.id())};
dojo.query('[name=noteslink]', row)[0].onclick = function() {self.drawLiNotes(li)};
+ /* XXX note how checkClaimEligibility() is getting called once per LI
+ * when in some use cases we should be able to do that job with one
+ * call per PO or per PL or whatever... */
+ this._fetchLineitem(
+ li.id(), function(full) {
+ self.liCache[full.id()] = full;
+ self.checkClaimEligibility(full, row);
+ }
+ );
+
this.updateLiNotesCount(li, row);
this.setClaimPolicyControl(li, row);
}
};
+ this._liCountClaims = function(li) {
+ var total = 0;
+ for (var i = 0; i < li.lineitem_details().length; i++)
+ total += li.lineitem_details()[i].claims().length;
+ return total;
+ };
+
+ this.reconsiderClaimControl = function(li, row) {
+ var option = nodeByName("action_manage_claims", row);
+ var eligible = this.claimEligibleLidByLi[li.id()].length;
+ var count = this._liCountClaims(li);
+
+ option.disabled = !(count || eligible);
+
+ /* of course I'd rather just populate a <span> element inside the
+ * option element, but it seems you can't actually have any elements
+ * inside option elements */
+ option.innerHTML = option.innerHTML.replace(
+ /(^.+)(.*)( existing.+$)/, "$1" + String(count) + "$3"
+ );
+ option.onclick = function() { self.claimDialog.show(li); };
+ };
+
+ this.checkClaimEligibility = function(li, row) {
+ this.claimEligibleLidByLi[li.id()] = [];
+ fieldmapper.standardRequest(
+ ["open-ils.acq", "open-ils.acq.claim.eligible.lineitem_detail"], {
+ "params": [openils.User.authtoken, {"lineitem": li.id()}],
+ "async": true,
+ "onresponse": function(r) {
+ if (r = openils.Util.readResponse(r)) {
+ self.claimEligibleLidByLi[li.id()].push(
+ r.lineitem_detail()
+ );
+ }
+ },
+ "oncomplete": function() {
+ self.reconsiderClaimControl(li, row);
+ }
+ }
+ );
+ };
+
this.updateLiNotesCount = function(li, row) {
if (typeof(row) == "undefined")
row = dojo.query('tr[li="' + li.id() + '"]', "acq-lit-tbody")[0];
}
);
+ dojo.byId("acq-eligible-claim-submit").onclick = function() {
+ finalClaimDialog.hide();
+ self.claim(self.getSelected());
+ };
+
+ dojo.query("button[name='claim_submit']").forEach(
+ function(button) {
+ button.onclick = function() {
+ if (self.getSelected().length)
+ finalClaimDialog.show();
+ else
+ alert(localeStrings.NO_LI_TO_CLAIM);
+ };
+ }
+ );
+
this.showEmpty = function() {
dojo.place(dojo.clone(this.emptyTemplate), this.tBody, "only");
openils.Util.hide("acq-eligible-claim-controls");
this.resetVoucher = function() { this.voucherWin = null; };
this.addToVoucher = function(contents) {
- if (!this.voucherWin) {
- this.voucherWin = window.open(
- "", "", "resizable,width=800,height=600,scrollbars=1"
- );
- this.voucherWin.document.title = localeStrings.CLAIM_VOUCHERS;
- this.voucherWin.document.body.innerHTML = (
- "<button onclick='window.print();'>" +
- localeStrings.PRINT +
- "</button><hr /><div id='main'></div>"
- );
- }
- dojo.byId("main", this.voucherWin.document).innerHTML += (
- contents + "<hr />"
- );
+ if (!this.voucherWin)
+ this.voucherWin = openClaimVoucherWindow();
+ dojo.byId("main", this.voucherWin.document).innerHTML +=
+ (contents + "<hr />");
};
- this.claim = function() {
- var lineitems = this.getSelected();
- if (!lineitems.length) {
- alert(localeStrings.NO_LI_TO_CLAIM);
- return;
- }
+ this.finishVoucher = function() {
+ var print_btn = dojo.byId("print", this.voucherWin.document);
+ print_btn.disabled = false;
+ print_btn.innerHTML = localeStrings.PRINT;
+ };
+ this.claim = function(lineitems) {
progressDialog.show(true);
self.resetVoucher();
);
if (!nodeByName("selector", self.tBody)) // emptiness test
self.showEmpty();
+
+ self.finishVoucher();
progressDialog.hide();
}
}
--- /dev/null
+function openClaimVoucherWindow() {
+ var win = window.open(
+ "", "", "resizable,width=800,height=600,scrollbars=1"
+ );
+ /* XXX i18n - also, the reason this isn't loaded from a server-side page
+ * has to do with problems of knowing when the page has loaded, so we
+ * can begin maniuplating it. we could retrieve content with an xhr call
+ * though...
+ */
+ win.document.title = "Claim Voucher";
+ win.document.body.innerHTML =
+ '<h1>Claim Voucher</h1>' +
+ '<div>' +
+ '<button id="print" onclick="window.print();" disabled="disabled">' +
+ 'Loading...' +
+ '</button>' +
+ '</div>' +
+ '<hr />' +
+ '<div id="main"></div>'
+ ;
+ return win;
+};
--- /dev/null
+<div style="width: 300px; height: 300px; overflow: auto;">
+ <script
+ src="[% ctx.media_prefix %]/js/ui/default/acq/common/claim_dialog.js">
+ </script>
+ <div><big>Claims</big></div>
+ <div>Against item:
+ <span id="acq-lit-li-claim-dia-li-title"></span>
+ (<span id="acq-lit-li-claim-dia-li-id"></span>)
+ </div>
+ <div id="acq-lit-li-claim-dia-show" class="hidden">
+ <ul id="acq-lit-li-claim-dia-lid-list">
+ <li name="lid">
+ <span name="barcode"></span> /
+ <span name="recvd"></span>
+ <ul name="claims">
+ <li name="claim">
+ <span name="type"></span>
+ <a name="voucher"
+ href="javascript:void(0);">Show Voucher</a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <div id="acq-lit-li-claim-dia-initiate" class="hidden">
+ <hr />
+ <div><big>Initiate New Claims</big></div>
+ <div id="acq-lit-li-claim-dia-lid-list-init">
+ <div name="lid_to_claim">
+ <input type="checkbox" name="claimable_lid" />
+ <label name="claimable_lid_label">
+ <span name="barcode"></span> /
+ <span name="recvd"></span>
+ </label>
+ </div>
+ </div>
+ <button id="acq-lit-li-claim-dia-claim">Claim selected</button>
+ </div>
+</div>
--- /dev/null
+<div id="acq-eligible-claim-controls">
+ <label for="acq-eligible-claim-type">Claim type:</label>
+ <span id="acq-eligible-claim-type"></span>
+ <label for="acq-eligible-claim-note">Note:</label>
+ <input dojoType="dijit.form.TextBox" id="acq-eligible-claim-note" />
+ <button id="acq-eligible-claim-submit">Claim</button>
+</div>
+
<script src="[% ctx.media_prefix %]/js/ui/default/acq/common/base64.js"> </script>
<script src='[% ctx.media_prefix %]/js/ui/default/acq/common/li_table.js'> </script>
+<script src='[% ctx.media_prefix %]/js/ui/default/acq/financial/claim_voucher.js'> </script>
<div id='acq-lit-table-container'>
<div id='acq-lit-table-div' class='hidden'>
<option name='action_link_invoice' disabled='disabled'>Link to Invoice</option>
<option name='action_view_invoice' disabled='disabled'>View Invoice(s)</option>
<option name='action_view_claim_policy'>Apply Claim Policy</option>
+ <option name='action_manage_claims' disabled='disabled'>Manage Claims ( existing)</option>
</select>
</td>
<td><span name='li_state'></span></td>
jsId="acqLidCancelButton">Cancel Copy</span>
</div>
<div dojoType="dijit.Dialog" jsId="liClaimPolicyDialog">
- <label for="acq-lit-li-claim-policy">Claim policy:</label>
- <span id="acq-lit-li-claim-policy"></span>
- <span dojoType="dijit.form.Button"
- jsId="liClaimPolicySave">Save</span>
+ <label for="acq-lit-li-claim-policy">Claim policy:</label>
+ <span id="acq-lit-li-claim-policy"></span>
+ <span dojoType="dijit.form.Button"
+ jsId="liClaimPolicySave">Save</span>
+ </div>
+ <div dojoType="dijit.Dialog" jsId="liClaimDialog">
+ [% INCLUDE "default/acq/common/claim_dialog.tt2" %]
+ </div>
+ <div dojoType="dijit.Dialog" jsId="finalClaimDialog">
+ [% INCLUDE "default/acq/common/final_claim_dialog.tt2" %]
</div>
</div>
<script
src="[% ctx.media_prefix %]/js/ui/default/acq/financial/claim_eligible.js">
</script>
+<script
+ src="[% ctx.media_prefix %]/js/ui/default/acq/financial/claim_voucher.js">
+</script>
<div>
<h1>Items Eligible For Claiming</h1>
<div class="oils-acq-basic-roomy">
<span dojoType="openils.widget.ProgressDialog"
jsId="progressDialog"></span>
</div>
+ <div class="oils-acq-basic-roomy">
+ <button name="claim_submit">Claim selected items</button>
+ </div>
<table id="acq-eligible-li-table">
<thead>
<tr>
</tr>
</tbody>
</table>
- <div id="acq-eligible-claim-controls" class="hidden">
- <label for="acq-eligible-claim-type">Claim type:</label>
- <span id="acq-eligible-claim-type"></span>
- <label for="acq-eligible-claim-note">Note:</label>
- <input dojoType="dijit.form.TextBox" id="acq-eligible-claim-note" />
- <button onclick="eligibleLiTable.claim();">Claim selected items</button>
+ <div class="oils-acq-basic-roomy">
+ <button name="claim_submit">Claim selected items</button>
+ </div>
+</div>
+<div class="hidden">
+ <div dojoType="dijit.Dialog" jsId="finalClaimDialog">
+ [% INCLUDE "default/acq/common/final_claim_dialog.tt2" %]
</div>
</div>
[% END %]