RewriteCond %{QUERY_STRING} (^.*$)
RewriteRule ^/openurl$ ${openurl:%1} [NE,PT]
+
+
+# General Evergreen web template processor
+<Location /eg>
+ SetHandler perl-script
+ PerlHandler OpenILS::WWW::EGWeb
+ Options +ExecCGI
+ PerlSendHeader On
+ allow from all
+</Location>
+# Note: the template processor will decline handling anything it does not
+# have an explicit configuration for, which means it will fall back to
+# Apache to serve the file. However, in the interest of speed, go ahead
+# and tell Apache to avoid asking OpenILS::WWW::EGWeb for static content.
+# Add more exemptions as needed.
+<LocationMatch ^/eg/.*(\.js|\.css|\.html|\.xhtml|\.xml|\.jpg|\.png|\.gif)$>
+ SetHandler None
+</LocationMatch>
+
+
<?xml version="1.0" encoding="UTF-8" ?>
-
<!--
Copyright (C) 2006-2008 Georgia Public Library Service
</links>
<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
<actions>
- <create permission="TRANSIT_COPY"/>
+ <create permission="TRANSIT_COPY" context_field="owner">
+ <context link="target_copy" field="circ_lib"/>
+ </create>
<retrieve/>
<update permission="UPDATE_TRANSIT" context_field="dest|source"/>
<delete permission="DELETE_TRANSIT" context_field="dest|source"/>
</actions>
</permacrud>
</class>
+
+ <class id="acqct" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::currency_type" oils_persist:tablename="acq.currency_type">
+ <fields oils_persist:primary="code">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Currency Code" name="code" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Currency Label" name="label" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true" />
+ </fields>
+ <links/>
+ </class>
+
+ <class id="acqexr" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::exchange_rate" oils_persist:tablename="acq.exchange_rate">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.exchange_rate_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Exchange Rate ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="From Currency" name="from_currency" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="To Currency" name="to_currency" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Ratio" name="ratio" oils_obj:array_position="6" oils_persist:virtual="false" />
+ </fields>
+ <links>
+ <link field="from_currency" reltype="has_a" key="code" map="" class="acqct"/>
+ <link field="to_currency" reltype="has_a" key="code" map="" class="acqct"/>
+ </links>
+ </class>
+
+ <class id="acqpro" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::provider" oils_persist:tablename="acq.provider">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.provider_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Provider ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Provider Name" name="name" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true" />
+ <field reporter:label="Owner" name="owner" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="org_unit" />
+ <field reporter:label="Currency" name="currency_type" oils_obj:array_position="6" oils_persist:virtual="false" oils_persist:primitive="string" reporter:datatype="link" />
+ <field reporter:label="Code" name="code" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="text" />
+ </fields>
+ <links>
+ <link field="currency_type" reltype="has_a" key="code" map="" class="acqct"/>
+ <link field="owner" reltype="has_a" key="id" map="" class="aou"/>
+ </links>
+ </class>
+
+ <class id="acqfs" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::funding_source" oils_persist:tablename="acq.funding_source">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.funding_source_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Funding Source ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Funding Source Name" name="name" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true" />
+ <field reporter:label="Owner" name="owner" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="org_unit" />
+ <field reporter:label="Currency" name="currency_type" oils_obj:array_position="6" oils_persist:virtual="false" oils_persist:primitive="string" reporter:datatype="link" />
+ <field reporter:label="Code" name="code" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="text" />
+ <field name="summary" oils_obj:array_position="8" oils_persist:virtual="true"/>
+ <field reporter:label="Allocations" name="allocations" oils_obj:array_position="9" oils_persist:virtual="true" reporter:datatype="link"/>
+ <field reporter:label="Credits" name="credits" oils_obj:array_position="10" oils_persist:virtual="true" reporter:datatype="link"/>
+ </fields>
+ <links>
+ <link field="currency_type" reltype="has_a" key="code" map="" class="acqct"/>
+ <link field="owner" reltype="has_a" key="id" map="" class="aou"/>
+ <link field="allocations" reltype="has_many" map="" key="funding_source" class="acqfa"/>
+ <link field="credits" reltype="has_many" key="funding_source" map="" class="acqfscred"/>
+ </links>
+ <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+ <actions>
+ <create permission="CREATE_ACQ_FUNDING_SOURCE" context_field="owner"/>
+ <retrieve permission="VIEW_ACQ_FUNDING_SOURCE" context_field="owner"/>
+ <update permission="UPDATE_ACQ_FUNDING_SOURCE" context_field="owner"/>
+ <delete permission="DELETE_ACQ_FUNDING_SOURCE" context_field="owner"/>
+ </actions>
+ </permacrud>
+ </class>
+
+ <class id="acqfscred" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::funding_source_credit" oils_persist:tablename="acq.funding_source_credit">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.funding_source_credit_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Credit ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Funding Source ID" name="funding_source" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Amount" name="amount" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="money" />
+ <field reporter:label="Note" name="note" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="text" />
+ </fields>
+ <links>
+ <link field="funding_source" reltype="has_a" key="id" map="" class="acqfs"/>
+ </links>
+ </class>
+
+ <class id="acqfdeb" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::fund_debit" oils_persist:tablename="acq.fund_debit">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.fund_debit_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Debit ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Fund ID" name="fund" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Origin Amount" name="origin_amount" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="money" />
+ <field reporter:label="Origin Currency" name="origin_currency_type" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Amount" name="amount" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="money" />
+ <field reporter:label="Encumbrance" name="encumbrance" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Debit Type" name="debit_type" oils_obj:array_position="9" oils_persist:virtual="false" reporter:datatype="text" />
+ </fields>
+ <links>
+ <link field="fund" reltype="has_a" key="id" map="" class="acqf"/>
+ <link field="origin_currency_type" reltype="has_a" key="code" map="" class="acqct"/>
+ </links>
+ </class>
+
+ <class id="acqf" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::fund" oils_persist:tablename="acq.fund">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.fund_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Fund ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Org Unit" name="org" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="org_unit" />
+ <field reporter:label="Name" name="name" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Year" name="year" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="int" />
+ <field reporter:label="Currency Type" name="currency_type" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Code" name="code" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="text" />
+ <field name="summary" oils_obj:array_position="9" oils_persist:virtual="true"/>
+ <field reporter:label="Allocations" name="allocations" oils_obj:array_position="10" oils_persist:virtual="true" reporter:datatype="link"/>
+ <field reporter:label="Debits" name="debits" oils_obj:array_position="11" oils_persist:virtual="true" reporter:datatype="link"/>
+ </fields>
+ <links>
+ <link field="org" reltype="has_a" key="id" map="" class="aou"/>
+ <link field="currency_type" reltype="has_a" key="code" map="" class="acqct"/>
+ <link field="allocations" reltype="has_many" key="fund" map="" class="acqfa"/>
+ <link field="debits" reltype="has_many" key="fund" map="" class="acqfdeb"/>
+ </links>
+ </class>
+
+ <class id="acqfat" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::fund_allocation_total" oils_persist:readonly="true" oils_persist:tablename="acq.fund_allocation_total">
+ <fields oils_persist:primary="fund">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Fund ID" name="fund" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Total Allocation Amount" name="amount" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="money" />
+ </fields>
+ <links>
+ <link field="fund" reltype="has_a" key="id" map="" class="acqf"/>
+ </links>
+ </class>
+
+ <class id="acqfdt" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::fund_debit_total" oils_persist:readonly="true" oils_persist:tablename="acq.fund_debit_total">
+ <fields oils_persist:primary="fund">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Fund ID" name="fund" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Total Debit Amount" name="amount" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="money" />
+ </fields>
+ <links>
+ <link field="fund" reltype="has_a" key="id" map="" class="acqf"/>
+ </links>
+ </class>
+
+ <class id="acqfet" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::fund_encumbrance_total" oils_persist:readonly="true" oils_persist:tablename="acq.fund_encumbrance_total">
+ <fields oils_persist:primary="fund">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Fund ID" name="fund" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Total Encumbrance Amount" name="amount" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="money" />
+ </fields>
+ <links>
+ <link field="fund" reltype="has_a" key="id" map="" class="acqf"/>
+ </links>
+ </class>
+
+ <class id="acqfst" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::fund_spent_total" oils_persist:readonly="true" oils_persist:tablename="acq.fund_spent_total">
+ <fields oils_persist:primary="fund">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Fund ID" name="fund" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Total Spent Amount" name="amount" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="money" />
+ </fields>
+ <links>
+ <link field="fund" reltype="has_a" key="id" map="" class="acqf"/>
+ </links>
+ </class>
+
+ <class id="acqfcb" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::fund_combined_balance" oils_persist:readonly="true" oils_persist:tablename="acq.fund_combined_balance">
+ <fields oils_persist:primary="fund">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Fund ID" name="fund" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Balance after Spent and Encumbered" name="amount" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="money" />
+ </fields>
+ <links>
+ <link field="fund" reltype="has_a" key="id" map="" class="acqf"/>
+ </links>
+ </class>
+
+ <class id="acqfsrcct" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::funding_source_credit_total" oils_persist:readonly="true" oils_persist:tablename="acq.funding_source_credit_total">
+ <fields oils_persist:primary="funding_source">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Funding Source" name="funding_source" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Total Credits to Funding Source" name="amount" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="money" />
+ </fields>
+ <links>
+ <link field="funding_source" reltype="has_a" key="id" map="" class="acqfs"/>
+ </links>
+ </class>
+
+ <class id="acqfsrcat" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::funding_source_allocation_total" oils_persist:readonly="true" oils_persist:tablename="acq.funding_source_allocation_total">
+ <fields oils_persist:primary="funding_source">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Funding Source" name="funding_source" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Total Allocated from Funding Source" name="amount" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="money" />
+ </fields>
+ <links>
+ <link field="funding_source" reltype="has_a" key="id" map="" class="acqfs"/>
+ </links>
+ </class>
+
+ <class id="acqfsrcb" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::funding_source_balance" oils_persist:readonly="true" oils_persist:tablename="acq.funding_source_balance">
+ <fields oils_persist:primary="funding_source">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Funding Source" name="funding_source" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Balance Remaining" name="amount" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="money" />
+ </fields>
+ <links>
+ <link field="funding_source" reltype="has_a" key="id" map="" class="acqfs"/>
+ </links>
+ </class>
+
+ <class id="acqfsb" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::fund_spent_balance" oils_persist:readonly="true" oils_persist:tablename="acq.fund_spent_balance">
+ <fields oils_persist:primary="fund">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Fund ID" name="fund" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Balance after Spent" name="amount" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="money" />
+ </fields>
+ <links>
+ <link field="fund" reltype="has_a" key="id" map="" class="acqf"/>
+ </links>
+ </class>
+
+ <class id="acqfa" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::fund_allocation" oils_persist:tablename="acq.fund_allocation">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.fund_allocation_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Allocation ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Fund" name="fund" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Funding Source" name="funding_source" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Amount" name="amount" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="money" />
+ <field reporter:label="Percent" name="percent" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="float" />
+ <field reporter:label="Allocating User" name="allocator" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Note" name="note" oils_obj:array_position="9" oils_persist:virtual="false" reporter:datatype="text" />
+ </fields>
+ <links>
+ <link field="allocator" reltype="has_a" key="id" map="" class="au"/>
+ <link field="fund" reltype="has_a" key="id" map="" class="acqf"/>
+ <link field="funding_source" reltype="has_a" key="id" map="" class="acqfs"/>
+ </links>
+ </class>
+
+ <class id="acqpl" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::picklist" oils_persist:tablename="acq.picklist">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.picklist_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Picklist ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Owner" name="owner" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Org Unit" name="org_unit" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Name" name="name" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true" />
+ <field reporter:label="Creation Time" name="create_time" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="timestamp" />
+ <field reporter:label="Edit Time" name="edit_time" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="timestamp" />
+ <field reporter:label="Entries" name="entries" oils_obj:array_position="9" oils_persist:virtual="true" reporter:datatype="link" />
+ <field reporter:label="Entry Count" name="entry_count" oils_obj:array_position="10" oils_persist:virtual="true"/>
+ </fields>
+ <links>
+ <link field="owner" reltype="has_a" key="id" map="" class="au"/>
+ <link field="org_unit" reltype="has_a" key="id" map="" class="aou"/>
+ <link field="entries" reltype="has_many" key="picklist" map="" class="jub"/>
+ </links>
+ </class>
+
+ <class id="acqpo" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::purchase_order" oils_persist:tablename="acq.purchase_order">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.purchase_order_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Purchase Order ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Owner" name="owner" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Creation Time" name="create_time" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="timestamp" />
+ <field reporter:label="Edit Time" name="edit_time" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="timestamp" />
+ <field reporter:label="Provider" name="provider" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="State" name="state" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Ordering Agency" name="ordering_agency" oils_obj:array_position="9" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Line Items" name="lineitems" oils_obj:array_position="10" oils_persist:virtual="true" reporter:datatype="link" />
+ <field reporter:label="Line Item Count" name="lineitem_count" oils_obj:array_position="11" oils_persist:virtual="true" reporter:datatype="link" />
+ </fields>
+ <links>
+ <link field="owner" reltype="has_a" key="id" map="" class="au"/>
+ <link field="default_fund" reltype="has_a" key="id" map="" class="acqf"/>
+ <link field="provider" reltype="has_a" key="id" map="" class="acqpro"/>
+ <link field="lineitems" reltype="has_many" key="purchase_order" map="" class="jub"/>
+ <link field="ordering_agency" reltype="has_a" key="id" map="" class="aou"/>
+ </links>
+ </class>
+
+ <class id="acqpon" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::po_note" oils_persist:tablename="acq.po_note">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.po_note_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="PO Note ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Purchase Order" name="purchase_order" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Creator" name="creator" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Creation Time" name="create_time" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="timestamp" />
+ <field reporter:label="Edit Time" name="edit_time" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="timestamp" />
+ <field reporter:label="Editor" name="editor" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Vote Value" name="value" oils_obj:array_position="9" oils_persist:virtual="false" reporter:datatype="text" />
+ </fields>
+ <links>
+ <link field="creator" reltype="has_a" key="id" map="" class="au"/>
+ <link field="editor" reltype="has_a" key="id" map="" class="au"/>
+ <link field="purchase_order" reltype="has_a" key="id" map="" class="acqpo"/>
+ </links>
+ </class>
+
+ <class id="jub" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::lineitem" oils_persist:tablename="acq.lineitem">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.lineitem_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Lineitem ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Selecting Org Unit" name="selector" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Picklist" name="picklist" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Purchase Order" name="purchase_order" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Provider" name="provider" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Creation Time" name="create_time" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="timestamp" />
+ <field reporter:label="Edit Time" name="edit_time" oils_obj:array_position="9" oils_persist:virtual="false" reporter:datatype="timestamp" />
+ <field reporter:label="MARC" name="marc" oils_obj:array_position="10" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Evergreen Bib ID" name="eg_bib_id" oils_obj:array_position="11" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Source Label" name="source_label" oils_obj:array_position="12" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Expected Receive Date" name="expected_recv_time" oils_obj:array_position="13" oils_persist:virtual="false" reporter:datatype="timestamp" />
+ <field reporter:label="State" name="state" oils_obj:array_position="14" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Item Count" name="item_count" oils_obj:array_position="15" oils_persist:virtual="true" reporter:datatype="int" />
+ <field reporter:label="Descriptive Attributes" name="attributes" oils_obj:array_position="16" oils_persist:virtual="true" reporter:datatype="link" />
+ <field reporter:label="Line Item Details" name="lineitem_details" oils_obj:array_position="17" oils_persist:virtual="true" reporter:datatype="link" />
+ <field reporter:label="Line Item Notes" name="lineitem_notes" oils_obj:array_position="18" oils_persist:virtual="true" reporter:datatype="link" />
+ </fields>
+ <links>
+ <link field="selector" reltype="has_a" key="id" map="" class="aou"/>
+ <link field="provider" reltype="has_a" key="id" map="" class="acqpro"/>
+ <link field="purchase_order" reltype="has_a" key="id" map="" class="acqpo"/>
+ <link field="picklist" reltype="has_a" key="id" map="" class="acqpl"/>
+ <link field="eg_bib_id" reltype="has_a" key="id" map="" class="bre"/>
+ <link field="attributes" reltype="has_many" key="lineitem" map="" class="acqlia"/>
+ <link field="lineitem_details" reltype="has_many" key="lineitem" map="" class="acqlid"/>
+ <link field="lineitem_notes" reltype="has_many" key="lineitem" map="" class="acqlin"/>
+ </links>
+ </class>
+
+ <class id="acqlin" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::lineitem_note" oils_persist:tablename="acq.lineitem_note">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.lineitem_note_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="PO Line Item Note ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Line Item" name="lineitem" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Creator" name="creator" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Creation Time" name="create_time" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="timestamp" />
+ <field reporter:label="Edit Time" name="edit_time" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="timestamp" />
+ <field reporter:label="Editor" name="editor" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Vote Value" name="value" oils_obj:array_position="9" oils_persist:virtual="false" reporter:datatype="text" />
+ </fields>
+ <links>
+ <link field="creator" reltype="has_a" key="id" map="" class="au"/>
+ <link field="editor" reltype="has_a" key="id" map="" class="au"/>
+ <link field="lineitem" reltype="has_a" key="id" map="" class="jub"/>
+ </links>
+ </class>
+
+ <class id="acqlia" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::lineitem_attr" oils_persist:tablename="acq.lineitem_attr">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.lineitem_attr_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Attribute Value ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Lineitem" name="lineitem" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Type" name="attr_type" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Name" name="attr_name" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Value" name="attr_value" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Definition" name="definition" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="text" />
+ </fields>
+ <links>
+ <link field="lineitem" reltype="has_a" key="id" map="" class="jub"/>
+ <link field="definition" reltype="has_a" key="id" map="" class="acqliad"/>
+ </links>
+ </class>
+
+ <class id="acqlid" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::lineitem_detail" oils_persist:tablename="acq.lineitem_detail">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.lineitem_detail_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Item Detail ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="PO Line Item" name="lineitem" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Evergreen Copy ID" name="eg_copy_id" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Barcode" name="barcode" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Call Number Label" name="cn_label" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Actual Receive Date" name="recv_time" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="timestamp" />
+ <field reporter:label="Fund" name="fund" oils_obj:array_position="9" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Fund Debit" name="fund_debit" oils_obj:array_position="10" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Owning Library" name="owning_lib" oils_obj:array_position="11" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Shelving Location" name="location" oils_obj:array_position="12" oils_persist:virtual="false" reporter:datatype="link" />
+ </fields>
+ <links>
+ <link field="lineitem" reltype="has_a" key="id" map="" class="jub"/>
+ <link field="eg_copy_id" reltype="has_a" key="id" map="" class="acp"/>
+ <link field="fund" reltype="has_a" key="id" map="" class="acqf"/>
+ <link field="fund_debit" reltype="has_a" key="id" map="" class="acqfdeb"/>
+ <link field="owning_lib" reltype="has_a" key="id" map="" class="aou"/>
+ <link field="location" reltype="has_a" key="id" map="" class="acpl"/>
+ </links>
+ </class>
+
+ <class id="acqliad" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::lineitem_attr_definition" oils_persist:tablename="acq.lineitem_attr_definition">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.lineitem_attr_definition_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Definition ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Code" name="code" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Description" name="description" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true" />
+ <field reporter:label="Is Identifier?" name="ident" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="bool"/>
+ </fields>
+ <links/>
+ </class>
+
+ <class id="acqlimad" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::lineitem_marc_attr_definition" oils_persist:tablename="acq.lineitem_marc_attr_definition">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.lineitem_attr_definition_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Definition ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Code" name="code" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Description" name="description" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true" />
+ <field reporter:label="XPath" name="xpath" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Is Identifier?" name="ident" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="bool"/>
+ </fields>
+ <links/>
+ </class>
+
+ <class id="acqligad" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::lineitem_generated_attr_definition" oils_persist:tablename="acq.lineitem_generated_attr_definition">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.lineitem_attr_definition_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Definition ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Code" name="code" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Description" name="description" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true" />
+ <field reporter:label="XPath" name="xpath" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Is Identifier?" name="ident" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="bool"/>
+ </fields>
+ <links/>
+ </class>
+
+ <class id="acqlipad" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::lineitem_provider_attr_definition" oils_persist:tablename="acq.lineitem_provider_attr_definition">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.lineitem_attr_definition_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Definition ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Code" name="code" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Description" name="description" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true" />
+ <field reporter:label="XPath" name="xpath" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Provider" name="provider" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Is Identifier?" name="ident" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="bool"/>
+ </fields>
+ <links>
+ <link field="provider" reltype="has_a" key="id" map="" class="acqpro"/>
+ </links>
+ </class>
+
+ <class id="acqliuad" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::lineitem_usr_attr_definition" oils_persist:tablename="acq.lineitem_usr_attr_definition">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.lineitem_attr_definition_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Definition ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Code" name="code" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Description" name="description" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true" />
+ <field reporter:label="User" name="usr" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="link" />
+ <field reporter:label="Is Identifier?" name="ident" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="bool"/>
+ </fields>
+ <links>
+ <link field="usr" reltype="has_a" key="id" map="" class="au"/>
+ </links>
+ </class>
+
+ <class id="acqlilad" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::lineitem_local_attr_definition" oils_persist:tablename="acq.lineitem_local_attr_definition">
+ <fields oils_persist:primary="id" oils_persist:sequence="acq.lineitem_attr_definition_id_seq">
+ <field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+ <field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+ <field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+ <field reporter:label="Definition ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+ <field reporter:label="Code" name="code" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" />
+ <field reporter:label="Description" name="description" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true" />
+ <field reporter:label="Is Identifier?" name="ident" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="bool"/>
+ </fields>
+ <links/>
+ </class>
+
<class id="rof" controller="open-ils.reporter-store" oils_obj:fieldmapper="reporter::output_folder" oils_persist:tablename="reporter.output_folder">
<fields oils_persist:primary="id" oils_persist:sequence="reporter.output_folder_id_seq">
<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
<!-- ********************************************************************************************************************* -->
</IDL>
+
+<!--
+ vim:noet:ts=4:sw=4:
+-->
--- /dev/null
+<oils_web>
+ <!-- This should match the Apache Directory/Location[Match] configuration path -->
+ <base_uri>/eg</base_uri>
+
+ <!-- media_prefix can be a remote server.
+ E.g. <media_prefix>http://static.example.com/media</media_prefix> -->
+ <media_prefix/>
+
+ <!-- Where templates can be found. Paths will be checked in the order entered here.
+ It's possible to override individual or sets of templates by putting them into
+ a path in front of the default template path -->
+ <template_paths>
+ <!-- XXX we should really move these out of the default web root -->
+ <path>/openils/var/web/templates</path>
+ </template_paths>
+
+ <handlers>
+ <handler path='acq/picklist/list' template='acq/picklist/list.tt2'/>
+ <handler path='acq/picklist/view' template='acq/picklist/view.tt2'/>
+ <handler path='acq/picklist/bib_search' template='acq/picklist/bib_search.tt2'/>
+ <handler path='acq/fund/list' template='acq/financial/list_funds.tt2'/>
+ <handler path='acq/fund/view' template='acq/financial/view_fund.tt2'/>
+ <handler path='acq/funding_source/list' template='acq/financial/list_funding_sources.tt2'/>
+ <handler path='acq/funding_source/view' template='acq/financial/view_funding_source.tt2'/>
+ <handler path='acq/currency_type/list' template='acq/financial/list_currency_types.tt2'/>
+ <handler path='acq/currency_type/view' template='acq/financial/view_currency_type.tt2'/>
+ <handler path='acq/provider/list' template='acq/financial/list_providers.tt2'/>
+ <handler path='acq/provider/view' template='acq/financial/view_provider.tt2'/>
+ <handler path='acq/po/view' template='acq/po/view.tt2'/>
+ <handler path='acq/po/li_search' template='acq/po/li_search.tt2'/>
+ <handler path='acq/po/search' template='acq/po/search.tt2'/>
+ <handler path='acq/receiving/process' template='acq/receiving/process.tt2'/>
+ <handler path='acq/settings/li_attr' template='acq/settings/li_attr.tt2'/>
+ </handlers>
+</oils_web>
</event>
-
+ <event code='1841' textcode='ACQ_PICKLIST_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.picklist was not found</desc>
+ </event>
+ <event code='1843' textcode='ACQ_LINEITEM_ATTR_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.lineitem_attr was not found</desc>
+ </event>
+ <event code='1844' textcode='ACQ_FUNDING_SOURCE_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.funding_source was not found</desc>
+ </event>
+ <event code='1845' textcode='ACQ_PROVIDER_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.provider was not found</desc>
+ </event>
+ <event code='1846' textcode='ACQ_FUNDING_SOURCE_CREDIT_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.funding_source_credit was not found</desc>
+ </event>
+ <event code='1847' textcode='ACQ_FUNDING_SOURCE_DEDIT_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.funding_source_dedit was not found</desc>
+ </event>
+ <event code='1848' textcode='ACQ_FUND_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.fund was not found</desc>
+ </event>
+ <event code='1849' textcode='ACQ_FUND_DEBIT_TOTAL_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.fund_debit_total was not found</desc>
+ </event>
+ <event code='1850' textcode='ACQ_FUND_ALLOCATION_TOTAL_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.fund_allocation_total was not found</desc>
+ </event>
+ <event code='1851' textcode='ACQ_FUND_ENCUMBRANCE_TOTAL_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.fund_encumbrance_total was not found</desc>
+ </event>
+ <event code='1852' textcode='ACQ_FUND_SPENT_TOTAL_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.fund_spent_total was not found</desc>
+ </event>
+ <event code='1853' textcode='ACQ_FUND_COMBINED_BALANCE_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.fund_combined_balance was not found</desc>
+ </event>
+ <event code='1854' textcode='ACQ_FUND_SPENT_BALANCE_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.fund_spent_balance was not found</desc>
+ </event>
+ <event code='1855' textcode='ACQ_FUNDING_SOURCE_CREDIT_TOTAL_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.funding_source_credit_total was not found</desc>
+ </event>
+ <event code='1856' textcode='ACQ_FUNDING_SOURCE_ALLOCATION_TOTAL_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.funding_source_allocation_total was not found</desc>
+ </event>
+ <event code='1857' textcode='ACQ_FUNDING_SOURCE_BALANCE_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.funding_source_balance was not found</desc>
+ </event>
+ <event code='1858' textcode='ACQ_LINEITEM_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.po_lineitem was not found</desc>
+ </event>
+ <event code='1859' textcode='ACQ_PURCHASE_ORDER_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.purchase_order was not found</desc>
+ </event>
+ <event code='1870' textcode='ACQ_LINEITEM_DETAIL_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.lineitem_detail was not found</desc>
+ </event>
+ <event code='1871' textcode='PERMISSION_USR_OBJECT_PERM_MAP_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested permission.usr_object_perm_map was not found</desc>
+ </event>
+ <event code='1872' textcode='ACQ_LINEITEM_PROVIDER_ATTR_DEFINITION_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.lineitem_provider_attr_definition was not found</desc>
+ </event>
+ <event code='1873' textcode='ACQ_CURRENCY_TYPE_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq.currency_type was not found</desc>
+ </event>
+ <event code='1875' textcode='ACQ_LINEITEM_GENERATED_ATTR_DEFINITION_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq_lineitem_attr was not found</desc>
+ </event>
+ <event code='1876' textcode='ACQ_LINEITEM_LOCAL_ATTR_DEFINITION_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq_lineitem_attr was not found</desc>
+ </event>
+ <event code='1877' textcode='ACQ_LINEITEM_MARC_ATTR_DEFINITION_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq_lineitem_attr was not found</desc>
+ </event>
+ <event code='1878' textcode='ACQ_LINEITEM_USR_ATTR_DEFINITION_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq_lineitem_attr was not found</desc>
+ </event>
+ <event code='1879' textcode='ACQ_LINEITEM_PROVIDER_ATTR_DEFINITION_NOT_FOUND'>
+ <desc xml:lang='en-US'>The requested acq_lineitem_attr was not found</desc>
+ </event>
<event code='1700' textcode='NON_CAT_TYPE_EXISTS'>
<desc xml:lang="en-US"> A report with the given name and folder already exists</desc>
</event>
+ <event code='10000' textcode='ACQ_LINEITEM_APPROVED'>
+ <desc xml:lang="en-US">The lineitem cannot be altered because it has already been approved</desc>
+ </event>
+ <event code='10001' textcode='ACQ_LINEITEM_NO_COPIES'>
+ <desc xml:lang="en-US">The lineitem has no attached copies</desc>
+ </event>
+ <event code='10002' textcode='ACQ_LINEITEM_DETAIL_NO_FUND'>
+ <desc xml:lang="en-US">The lineitem detail has no associated fund</desc>
+ </event>
+ <event code='10003' textcode='ACQ_LINEITEM_DETAIL_NO_ORG'>
+ <desc xml:lang="en-US">The lineitem detail has no owning_lib</desc>
+ </event>
+ <event code='10004' textcode='ACQ_LINEITEM_NO_PRICE'>
+ <desc xml:lang="en-US">The lineitem has no price</desc>
+ </event>
+ <event code='10005' textcode='ACQ_LINEITEM_NO_PROVIDER'>
+ <desc xml:lang="en-US">The lineitem has no price</desc>
+ </event>
+
+
<!-- ================================================================ -->
--- /dev/null
+package OpenILS::Application::Acq;
+use base qw/OpenILS::Application/;
+use strict; use warnings;
+
+use OpenILS::Application::Acq::Picklist;
+use OpenILS::Application::Acq::Financials;
+use OpenILS::Application::Acq::Provider;
+use OpenILS::Application::Acq::Lineitem;
+
+1;
--- /dev/null
+package OpenILS::Application::Acq::Financials;
+use base qw/OpenILS::Application/;
+use strict; use warnings;
+
+use OpenSRF::Utils::Logger qw(:logger);
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Utils::CStoreEditor q/:funcs/;
+use OpenILS::Const qw/:const/;
+use OpenSRF::Utils::SettingsClient;
+use OpenILS::Event;
+use OpenILS::Application::AppUtils;
+my $U = 'OpenILS::Application::AppUtils';
+
+# ----------------------------------------------------------------------------
+# Funding Sources
+# ----------------------------------------------------------------------------
+
+__PACKAGE__->register_method(
+ method => 'create_funding_source',
+ api_name => 'open-ils.acq.funding_source.create',
+ signature => {
+ desc => 'Creates a new funding_source',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'funding source object to create', type => 'object'}
+ ],
+ return => {desc => 'The ID of the new funding_source'}
+ }
+);
+
+sub create_funding_source {
+ my($self, $conn, $auth, $funding_source) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+ return $e->die_event unless $e->allowed('ADMIN_FUNDING_SOURCE', $funding_source->owner);
+ $e->create_acq_funding_source($funding_source) or return $e->die_event;
+ $e->commit;
+ return $funding_source->id;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'delete_funding_source',
+ api_name => 'open-ils.acq.funding_source.delete',
+ signature => {
+ desc => 'Deletes a funding_source',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'funding source ID', type => 'number'}
+ ],
+ return => {desc => '1 on success, Event on failure'}
+ }
+);
+
+sub delete_funding_source {
+ my($self, $conn, $auth, $funding_source_id) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+ my $funding_source = $e->retrieve_acq_funding_source($funding_source_id) or return $e->die_event;
+ return $e->die_event unless $e->allowed('ADMIN_FUNDING_SOURCE', $funding_source->owner, $funding_source);
+ $e->delete_acq_funding_source($funding_source) or return $e->die_event;
+ $e->commit;
+ return 1;
+}
+
+__PACKAGE__->register_method(
+ method => 'retrieve_funding_source',
+ api_name => 'open-ils.acq.funding_source.retrieve',
+ signature => {
+ desc => 'Retrieves a new funding_source',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'funding source ID', type => 'number'}
+ ],
+ return => {desc => 'The funding_source object on success, Event on failure'}
+ }
+);
+
+sub retrieve_funding_source {
+ my($self, $conn, $auth, $funding_source_id, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ $options ||= {};
+
+ my $flesh = {flesh => 1, flesh_fields => {acqfs => []}};
+ push(@{$flesh->{flesh_fields}->{acqfs}}, 'credits') if $$options{flesh_credits};
+ push(@{$flesh->{flesh_fields}->{acqfs}}, 'allocations') if $$options{flesh_allocations};
+
+ my $funding_source = $e->retrieve_acq_funding_source([$funding_source_id, $flesh]) or return $e->event;
+
+ return $e->event unless $e->allowed(
+ ['ADMIN_FUNDING_SOURCE','MANAGE_FUNDING_SOURCE', 'VIEW_FUNDING_SOURCE'],
+ $funding_source->owner, $funding_source);
+
+ $funding_source->summary(retrieve_funding_source_summary_impl($e, $funding_source))
+ if $$options{flesh_summary};
+ return $funding_source;
+}
+
+__PACKAGE__->register_method(
+ method => 'retrieve_org_funding_sources',
+ api_name => 'open-ils.acq.funding_source.org.retrieve',
+ stream => 1,
+ signature => {
+ desc => 'Retrieves all the funding_sources associated with an org unit that the requestor has access to see',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'List of org Unit IDs. If no IDs are provided, this method returns the
+ full set of funding sources this user has permission to view', type => 'number'},
+ {desc => q/Limiting permission. this permission is used find the work-org tree from which
+ the list of orgs is generated if no org ids are provided.
+ The default is ADMIN_FUNDING_SOURCE/, type => 'string'},
+ ],
+ return => {desc => 'The funding_source objects on success, empty array otherwise'}
+ }
+);
+
+sub retrieve_org_funding_sources {
+ my($self, $conn, $auth, $org_id_list, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ $options ||= {};
+
+ my $limit_perm = ($$options{limit_perm}) ? $$options{limit_perm} : 'ADMIN_FUNDING_SOURCE';
+ return OpenILS::Event->new('BAD_PARAMS')
+ unless $limit_perm =~ /(ADMIN|MANAGE|VIEW)_FUNDING_SOURCE/;
+
+ my $org_ids = ($org_id_list and @$org_id_list) ? $org_id_list :
+ $U->find_highest_work_orgs($e, $limit_perm, {descendants =>1});
+
+ return [] unless @$org_ids;
+ my $sources = $e->search_acq_funding_source({owner => $org_ids});
+
+ for my $source (@$sources) {
+ $source->summary(retrieve_funding_source_summary_impl($e, $source))
+ if $$options{flesh_summary};
+ $conn->respond($source);
+ }
+
+ return undef;
+}
+
+sub retrieve_funding_source_summary_impl {
+ my($e, $source) = @_;
+ my $at = $e->search_acq_funding_source_allocation_total({funding_source => $source->id})->[0];
+ my $b = $e->search_acq_funding_source_balance({funding_source => $source->id})->[0];
+ my $ct = $e->search_acq_funding_source_credit_total({funding_source => $source->id})->[0];
+ return {
+ allocation_total => ($at) ? $at->amount : 0,
+ balance => ($b) ? $b->amount : 0,
+ credit_total => ($ct) ? $ct->amount : 0,
+ };
+}
+
+
+__PACKAGE__->register_method(
+ method => 'create_funding_source_credit',
+ api_name => 'open-ils.acq.funding_source_credit.create',
+ signature => {
+ desc => 'Create a new funding source credit',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'funding source credit object', type => 'object'}
+ ],
+ return => {desc => 'The ID of the new funding source credit on success, Event on failure'}
+ }
+);
+
+sub create_funding_source_credit {
+ my($self, $conn, $auth, $fs_credit) = @_;
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->event unless $e->checkauth;
+
+ my $fs = $e->retrieve_acq_funding_source($fs_credit->funding_source)
+ or return $e->die_event;
+ return $e->die_event unless $e->allowed(['MANAGE_FUNDING_SOURCE'], $fs->owner, $fs);
+
+ $e->create_acq_funding_source_credit($fs_credit) or return $e->die_event;
+ $e->commit;
+ return $fs_credit->id;
+}
+
+
+# ---------------------------------------------------------------
+# funds
+# ---------------------------------------------------------------
+
+__PACKAGE__->register_method(
+ method => 'create_fund',
+ api_name => 'open-ils.acq.fund.create',
+ signature => {
+ desc => 'Creates a new fund',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'fund object to create', type => 'object'}
+ ],
+ return => {desc => 'The ID of the newly created fund object'}
+ }
+);
+
+sub create_fund {
+ my($self, $conn, $auth, $fund) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+ return $e->die_event unless $e->allowed('ADMIN_FUND', $fund->org);
+ $e->create_acq_fund($fund) or return $e->die_event;
+ $e->commit;
+ return $fund->id;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'delete_fund',
+ api_name => 'open-ils.acq.fund.delete',
+ signature => {
+ desc => 'Deletes a fund',
+ params => {
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'fund ID', type => 'number'}
+ },
+ return => {desc => '1 on success, Event on failure'}
+ }
+);
+
+sub delete_fund {
+ my($self, $conn, $auth, $fund_id) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+ my $fund = $e->retrieve_acq_fund($fund_id) or return $e->die_event;
+ return $e->die_event unless $e->allowed('ADMIN_FUND', $fund->org, $fund);
+ $e->delete_acq_fund($fund) or return $e->die_event;
+ $e->commit;
+ return 1;
+}
+
+__PACKAGE__->register_method(
+ method => 'retrieve_fund',
+ api_name => 'open-ils.acq.fund.retrieve',
+ signature => {
+ desc => 'Retrieves a new fund',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'fund ID', type => 'number'}
+ ],
+ return => {desc => 'The fund object on success, Event on failure'}
+ }
+);
+
+sub retrieve_fund {
+ my($self, $conn, $auth, $fund_id, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ $options ||= {};
+
+ my $flesh = {flesh => 2, flesh_fields => {acqf => []}};
+ push(@{$flesh->{flesh_fields}->{acqf}}, 'debits') if $$options{flesh_debits};
+ push(@{$flesh->{flesh_fields}->{acqf}}, 'allocations') if $$options{flesh_allocations};
+ push(@{$flesh->{flesh_fields}->{acqfa}}, 'funding_source') if $$options{flesh_allocation_sources};
+
+ my $fund = $e->retrieve_acq_fund([$fund_id, $flesh]) or return $e->event;
+ return $e->event unless $e->allowed(['ADMIN_FUND','MANAGE_FUND', 'VIEW_FUND'], $fund->org, $fund);
+ $fund->summary(retrieve_fund_summary_impl($e, $fund))
+ if $$options{flesh_summary};
+ return $fund;
+}
+
+__PACKAGE__->register_method(
+ method => 'retrieve_org_funds',
+ api_name => 'open-ils.acq.fund.org.retrieve',
+ stream => 1,
+ signature => {
+ desc => 'Retrieves all the funds associated with an org unit',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'List of org Unit IDs. If no IDs are provided, this method returns the
+ full set of funding sources this user has permission to view', type => 'number'},
+ {desc => q/Options hash.
+ "limit_perm" -- this permission is used find the work-org tree from which
+ the list of orgs is generated if no org ids are provided. The default is ADMIN_FUND.
+ "flesh_summary" -- if true, the summary field on each fund is fleshed
+ The default is ADMIN_FUND/, type => 'string'},
+ ],
+ return => {desc => 'The fund objects on success, Event on failure'}
+ }
+);
+
+sub retrieve_org_funds {
+ my($self, $conn, $auth, $org_id_list, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ $options ||= {};
+
+ my $limit_perm = ($$options{limit_perm}) ? $$options{limit_perm} : 'ADMIN_FUND';
+ return OpenILS::Event->new('BAD_PARAMS')
+ unless $limit_perm =~ /(ADMIN|MANAGE|VIEW)_FUND/;
+
+ my $org_ids = ($org_id_list and @$org_id_list) ? $org_id_list :
+ $U->find_highest_work_orgs($e, $limit_perm, {descendants =>1});
+ return undef unless @$org_ids;
+ my $funds = $e->search_acq_fund({org => $org_ids});
+
+ for my $fund (@$funds) {
+ $fund->summary(retrieve_fund_summary_impl($e, $fund))
+ if $$options{flesh_summary};
+ $conn->respond($fund);
+ }
+
+ return undef;
+}
+
+__PACKAGE__->register_method(
+ method => 'retrieve_fund_summary',
+ api_name => 'open-ils.acq.fund.summary.retrieve',
+ signature => {
+ desc => 'Returns a summary of credits/debits/encumbrances for a fund',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'fund id', type => 'number' }
+ ],
+ return => {desc => 'A hash of summary information, Event on failure'}
+ }
+);
+
+sub retrieve_fund_summary {
+ my($self, $conn, $auth, $fund_id) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ my $fund = $e->retrieve_acq_fund($fund_id) or return $e->event;
+ return $e->event unless $e->allowed('MANAGE_FUND', $fund->org, $fund);
+ return retrieve_fund_summary_impl($e, $fund);
+}
+
+
+sub retrieve_fund_summary_impl {
+ my($e, $fund) = @_;
+
+ my $at = $e->search_acq_fund_allocation_total({fund => $fund->id})->[0];
+ my $dt = $e->search_acq_fund_debit_total({fund => $fund->id})->[0];
+ my $et = $e->search_acq_fund_encumbrance_total({fund => $fund->id})->[0];
+ my $st = $e->search_acq_fund_spent_total({fund => $fund->id})->[0];
+ my $cb = $e->search_acq_fund_combined_balance({fund => $fund->id})->[0];
+ my $sb = $e->search_acq_fund_spent_balance({fund => $fund->id})->[0];
+
+ return {
+ allocation_total => ($at) ? $at->amount : 0,
+ debit_total => ($dt) ? $dt->amount : 0,
+ encumbrance_total => ($et) ? $et->amount : 0,
+ spent_total => ($st) ? $st->amount : 0,
+ combined_balance => ($cb) ? $cb->amount : 0,
+ spent_balance => ($sb) ? $sb->amount : 0,
+ };
+}
+
+
+# ---------------------------------------------------------------
+# fund Allocations
+# ---------------------------------------------------------------
+
+__PACKAGE__->register_method(
+ method => 'create_fund_alloc',
+ api_name => 'open-ils.acq.fund_allocation.create',
+ signature => {
+ desc => 'Creates a new fund_allocation',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'fund allocation object to create', type => 'object'}
+ ],
+ return => {desc => 'The ID of the new fund_allocation'}
+ }
+);
+
+sub create_fund_alloc {
+ my($self, $conn, $auth, $fund_alloc) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ # this action is equivalent to both debiting a funding source and crediting a fund
+
+ my $source = $e->retrieve_acq_funding_source($fund_alloc->funding_source)
+ or return $e->die_event;
+ return $e->die_event unless $e->allowed('MANAGE_FUNDING_SOURCE', $source->owner);
+
+ my $fund = $e->retrieve_acq_fund($fund_alloc->fund) or return $e->die_event;
+ return $e->die_event unless $e->allowed('MANAGE_FUND', $fund->org, $fund);
+
+ $fund_alloc->allocator($e->requestor->id);
+ $e->create_acq_fund_allocation($fund_alloc) or return $e->die_event;
+ $e->commit;
+ return $fund_alloc->id;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'delete_fund_alloc',
+ api_name => 'open-ils.acq.fund_allocation.delete',
+ signature => {
+ desc => 'Deletes a fund_allocation',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'fund Alocation ID', type => 'number'}
+ ],
+ return => {desc => '1 on success, Event on failure'}
+ }
+);
+
+sub delete_fund_alloc {
+ my($self, $conn, $auth, $fund_alloc_id) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ my $fund_alloc = $e->retrieve_acq_fund_allocation($fund_alloc_id) or return $e->die_event;
+
+ my $source = $e->retrieve_acq_funding_source($fund_alloc->funding_source)
+ or return $e->die_event;
+ return $e->die_event unless $e->allowed('MANAGE_FUNDING_SOURCE', $source->owner, $source);
+
+ my $fund = $e->retrieve_acq_fund($fund_alloc->fund) or return $e->die_event;
+ return $e->die_event unless $e->allowed('MANAGE_FUND', $fund->org, $fund);
+
+ $e->delete_acq_fund_allocation($fund_alloc) or return $e->die_event;
+ $e->commit;
+ return 1;
+}
+
+__PACKAGE__->register_method(
+ method => 'retrieve_fund_alloc',
+ api_name => 'open-ils.acq.fund_allocation.retrieve',
+ signature => {
+ desc => 'Retrieves a new fund_allocation',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'fund Allocation ID', type => 'number'}
+ ],
+ return => {desc => 'The fund allocation object on success, Event on failure'}
+ }
+);
+
+sub retrieve_fund_alloc {
+ my($self, $conn, $auth, $fund_alloc_id) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ my $fund_alloc = $e->retrieve_acq_fund_allocation($fund_alloc_id) or return $e->event;
+
+ my $source = $e->retrieve_acq_funding_source($fund_alloc->funding_source)
+ or return $e->die_event;
+ return $e->die_event unless $e->allowed('MANAGE_FUNDING_SOURCE', $source->owner, $source);
+
+ my $fund = $e->retrieve_acq_fund($fund_alloc->fund) or return $e->die_event;
+ return $e->die_event unless $e->allowed('MANAGE_FUND', $fund->org, $fund);
+
+ return $fund_alloc;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'retrieve_funding_source_allocations',
+ api_name => 'open-ils.acq.funding_source.allocations.retrieve',
+ signature => {
+ desc => 'Retrieves a new fund_allocation',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'fund Allocation ID', type => 'number'}
+ ],
+ return => {desc => 'The fund allocation object on success, Event on failure'}
+ }
+);
+
+sub retrieve_funding_source_allocations {
+ my($self, $conn, $auth, $fund_alloc_id) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ my $fund_alloc = $e->retrieve_acq_fund_allocation($fund_alloc_id) or return $e->event;
+
+ my $source = $e->retrieve_acq_funding_source($fund_alloc->funding_source)
+ or return $e->die_event;
+ return $e->die_event unless $e->allowed('MANAGE_FUNDING_SOURCE', $source->owner, $source);
+
+ my $fund = $e->retrieve_acq_fund($fund_alloc->fund) or return $e->die_event;
+ return $e->die_event unless $e->allowed('MANAGE_FUND', $fund->org, $fund);
+
+ return $fund_alloc;
+}
+
+# ----------------------------------------------------------------------------
+# Currency
+# ----------------------------------------------------------------------------
+
+__PACKAGE__->register_method(
+ method => 'retrieve_all_currency_type',
+ api_name => 'open-ils.acq.currency_type.all.retrieve',
+ signature => {
+ desc => 'Retrieves all currency_type objects',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ ],
+ return => {desc => 'List of currency_type objects', type => 'list'}
+ }
+);
+
+sub retrieve_all_currency_type {
+ my($self, $conn, $auth, $fund_alloc_id) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ return $e->event unless $e->allowed('GENERAL_ACQ');
+ return $e->retrieve_all_acq_currency_type();
+}
+
+sub currency_conversion_impl {
+ my($src_currency, $dest_currency, $amount) = @_;
+ my $result = new_editor()->json_query({
+ select => {
+ acqct => [{
+ params => [$dest_currency, $amount],
+ transform => 'acq.exchange_ratio',
+ column => 'code',
+ alias => 'value'
+ }]
+ },
+ where => {code => $src_currency},
+ from => 'acqct'
+ });
+
+ return $result->[0]->{value};
+}
+
+
+# ----------------------------------------------------------------------------
+# Purchase Orders
+# ----------------------------------------------------------------------------
+
+__PACKAGE__->register_method(
+ method => 'create_purchase_order',
+ api_name => 'open-ils.acq.purchase_order.create',
+ signature => {
+ desc => 'Creates a new purchase order',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'purchase_order to create', type => 'object'}
+ ],
+ return => {desc => 'The purchase order id, Event on failure'}
+ }
+);
+
+sub create_purchase_order {
+ my($self, $conn, $auth, $p_order) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ $p_order->owner($e->requestor->id);
+ $p_order->ordering_agency($e->requestor->ws_ou);
+ return $e->die_event unless
+ $e->allowed('CREATE_PURCHASE_ORDER', $p_order->ordering_agency);
+
+ my $provider = $e->retrieve_acq_provider($p_order->provider)
+ or return $e->die_event;
+ return $e->die_event unless
+ $e->allowed('MANAGE_PROVIDER', $provider->owner, $provider);
+
+ $e->create_acq_purchase_order($p_order) or return $e->die_event;
+
+ $e->commit;
+ return $p_order->id;
+}
+
+
+# returns (price, type), where type=1 is local, type=2 is provider, type=3 is marc
+sub get_li_price {
+ my $li = shift;
+ my $attrs = $li->attributes;
+ my ($marc_estimated, $local_estimated, $local_actual, $prov_estimated, $prov_actual);
+
+ for my $attr (@$attrs) {
+ if($attr->attr_name eq 'estimated_price') {
+ $local_estimated = $attr->attr_value
+ if $attr->attr_type eq 'lineitem_local_attr_definition';
+ $prov_estimated = $attr->attr_value
+ if $attr->attr_type eq 'lineitem_prov_attr_definition';
+ $marc_estimated = $attr->attr_value
+ if $attr->attr_type eq 'lineitem_marc_attr_definition';
+
+ } elsif($attr->attr_name eq 'actual_price') {
+ $local_actual = $attr->attr_value
+ if $attr->attr_type eq 'lineitem_local_attr_definition';
+ $prov_actual = $attr->attr_value
+ if $attr->attr_type eq 'lineitem_prov_attr_definition';
+ }
+ }
+
+ return ($local_actual, 1) if $local_actual;
+ return ($prov_actual, 2) if $prov_actual;
+ return ($local_estimated, 1) if $local_estimated;
+ return ($prov_estimated, 2) if $prov_estimated;
+ return ($marc_estimated, 3);
+}
+
+
+__PACKAGE__->register_method(
+ method => 'create_purchase_order_debits',
+ api_name => 'open-ils.acq.purchase_order.debits.create',
+ signature => {
+ desc => 'Creates debits associated with a PO',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'purchase_order whose debits to create', type => 'number'},
+ {desc => 'arguments hash. Options include: encumbrance=bool', type => 'object'},
+ ],
+ return => {desc => 'The total amount of all created debits, Event on error'}
+ }
+);
+
+sub create_purchase_order_debits {
+ my($self, $conn, $auth, $po_id, $args) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ my $total = 0;
+ my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->die_event;
+ # XXX which perms?
+
+ my $li_ids = $e->search_acq_lineitem(
+ {purchase_order => $po_id},
+ {idlist => 1}
+ );
+
+ for my $li_id (@$li_ids) {
+ my $li = $e->retrieve_acq_lineitem([
+ $li_id,
+ { flesh => 1,
+ flesh_fields => {jub => ['attributes']},
+ }
+ ]);
+
+ my ($price, $ptype) = get_li_price($li);
+ unless($price) {
+ $e->rollback;
+ return OpenILS::Event->new('ACQ_LINEITEM_NO_PRICE', payload => $li->id);
+ }
+
+ unless($li->provider) {
+ $e->rollback;
+ return OpenILS::Event->new('ACQ_LINEITEM_NO_PROVIDER', payload => $li->id);
+ }
+
+ my $lid_ids = $e->search_acq_lineitem_detail(
+ {lineitem => $li->id},
+ {idlist=>1}
+ );
+
+ for my $lid_id (@$lid_ids) {
+ my $lid = $e->retrieve_acq_lineitem_detail([
+ $lid_id,
+ { flesh => 1,
+ flesh_fields => {acqlid => ['fund']}
+ }
+ ]);
+
+ my $debit = Fieldmapper::acq::fund_debit->new;
+ $debit->fund($lid->fund->id);
+ $debit->origin_amount($price);
+
+ if($ptype == 2) { # price from vendor
+ $debit->origin_currency_type($li->provider->currency_type);
+ $debit->amount(currency_conversion_impl(
+ $li->provider->currency_type, $lid->fund->currency_type, $price));
+ } else {
+ $debit->origin_currency_type($lid->fund->currency_type);
+ $debit->amount($price);
+ }
+
+ $debit->encumbrance($args->{encumbrance});
+ $debit->debit_type('purchase');
+ $e->create_acq_fund_debit($debit) or return $e->die_event;
+
+ # point the lineitem detail at the fund debit object
+ $lid->fund_debit($debit->id);
+ $lid->fund($lid->fund->id);
+ $e->update_acq_lineitem_detail($lid) or return $e->die_event;
+ $total += $debit->amount;
+ }
+ }
+
+ $e->commit;
+ return $total;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'retrieve_all_user_purchase_order',
+ api_name => 'open-ils.acq.purchase_order.user.all.retrieve',
+ stream => 1,
+ signature => {
+ desc => 'Retrieves a purchase order',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'purchase_order to retrieve', type => 'number'},
+ {desc => q/Options hash. flesh_lineitems: to get the lineitems and lineitem_attrs;
+ clear_marc: to clear the MARC data from the lineitem (for reduced bandwidth);
+ limit: number of items to return ,defaults to 50;
+ offset: offset in the list of items to return
+ order_by: sort the result, provide one or more colunm names, separated by commas,
+ optionally followed by ASC or DESC as a single string
+ li_limit : number of lineitems to return if fleshing line items;
+ li_offset : lineitem offset if fleshing line items
+ li_order_by : lineitem sort definition if fleshing line items
+ flesh_lineitem_detail_count : flesh lineitem_detail_count field
+ /,
+ type => 'hash'}
+ ],
+ return => {desc => 'The purchase order, Event on failure'}
+ }
+);
+
+sub retrieve_all_user_purchase_order {
+ my($self, $conn, $auth, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ $options ||= {};
+
+ # grab purchase orders I have
+ my $perm_orgs = $U->find_highest_work_orgs($e, 'MANAGE_PROVIDER', {descendants =>1});
+ return OpenILS::Event->new('PERM_FAILURE', ilsperm => 'MANAGE_PROVIDER')
+ unless @$perm_orgs;
+ my $provider_ids = $e->search_acq_provider({owner => $perm_orgs}, {idlist=>1});
+ my $po_ids = $e->search_acq_purchase_order({provider => $provider_ids}, {idlist=>1});
+
+ # grab my purchase orders
+ push(@$po_ids, @{$e->search_acq_purchase_order({owner => $e->requestor->id}, {idlist=>1})});
+
+ return undef unless @$po_ids;
+
+ # now get the db to limit/sort for us
+ $po_ids = $e->search_acq_purchase_order(
+ [ {id => $po_ids}, {
+ limit => $$options{limit} || 50,
+ offset => $$options{offset} || 0,
+ order_by => {acqpo => $$options{order_by} || 'create_time'}
+ }
+ ],
+ {idlist => 1}
+ );
+
+ $conn->respond(retrieve_purchase_order_impl($e, $_, $options)) for @$po_ids;
+ return undef;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'search_purchase_order',
+ api_name => 'open-ils.acq.purchase_order.search',
+ stream => 1,
+ signature => {
+ desc => 'Search for a purchase order',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => q/Search hash. Search fields include id, provider/, type => 'hash'}
+ ],
+ return => {desc => 'A stream of POs'}
+ }
+);
+
+sub search_purchase_order {
+ my($self, $conn, $auth, $search, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ my $po_ids = $e->search_acq_purchase_order($search, {idlist=>1});
+ for my $po_id (@$po_ids) {
+ $conn->respond($e->retrieve_acq_purchase_order($po_id))
+ unless po_perm_failure($e, $po_id);
+ }
+
+ return undef;
+}
+
+
+
+__PACKAGE__->register_method(
+ method => 'retrieve_purchase_order',
+ api_name => 'open-ils.acq.purchase_order.retrieve',
+ signature => {
+ desc => 'Retrieves a purchase order',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'purchase_order to retrieve', type => 'number'},
+ {desc => q/Options hash. flesh_lineitems, to get the lineitems and lineitem_attrs;
+ clear_marc, to clear the MARC data from the lineitem (for reduced bandwidth)
+ li_limit : number of lineitems to return if fleshing line items;
+ li_offset : lineitem offset if fleshing line items
+ li_order_by : lineitem sort definition if fleshing line items
+ /,
+ type => 'hash'}
+ ],
+ return => {desc => 'The purchase order, Event on failure'}
+ }
+);
+
+sub retrieve_purchase_order {
+ my($self, $conn, $auth, $po_id, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ return $e->event if po_perm_failure($e, $po_id);
+ return retrieve_purchase_order_impl($e, $po_id, $options);
+}
+
+
+# if the user does not have permission to perform actions on this PO, return the perm failure event
+sub po_perm_failure {
+ my($e, $po_id, $fund_id) = @_;
+ my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->event;
+ my $provider = $e->retrieve_acq_provider($po->provider) or return $e->event;
+ return $e->event unless $e->allowed('MANAGE_PROVIDER', $provider->owner, $provider);
+ if($fund_id) {
+ my $fund = $e->retrieve_acq_fund($po->$fund_id);
+ return $e->event unless $e->allowed('MANAGE_FUND', $fund->org, $fund);
+ }
+ return undef;
+}
+
+sub retrieve_purchase_order_impl {
+ my($e, $po_id, $options) = @_;
+
+ $options ||= {};
+ my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->event;
+
+ if($$options{flesh_lineitems}) {
+ my $items = $e->search_acq_lineitem([
+ {purchase_order => $po_id},
+ {
+ flesh => 1,
+ flesh_fields => {
+ jub => ['attributes']
+ },
+ limit => $$options{li_limit} || 50,
+ offset => $$options{li_offset} || 0,
+ order_by => {jub => $$options{li_order_by} || 'create_time'}
+ }
+ ]);
+
+ if($$options{clear_marc}) {
+ $_->clear_marc for @$items;
+ }
+
+ $po->lineitems($items);
+ }
+
+ if($$options{flesh_lineitem_count}) {
+ my $items = $e->search_acq_lineitem({purchase_order => $po_id}, {idlist=>1});
+ $po->lineitem_count(scalar(@$items));
+ }
+
+ return $po;
+}
+
+
+1;
+
--- /dev/null
+package OpenILS::Application::Acq::Picklist;
+use base qw/OpenILS::Application/;
+use strict; use warnings;
+
+use OpenILS::Event;
+use OpenSRF::Utils::Logger qw(:logger);
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Utils::CStoreEditor q/:funcs/;
+use OpenILS::Const qw/:const/;
+use OpenSRF::Utils::SettingsClient;
+use OpenILS::Application::AppUtils;
+my $U = 'OpenILS::Application::AppUtils';
+
+
+__PACKAGE__->register_method(
+ method => 'create_lineitem',
+ api_name => 'open-ils.acq.lineitem.create',
+ signature => {
+ desc => 'Creates a lineitem',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'The lineitem object to create', type => 'object'},
+ ],
+ return => {desc => 'ID of newly created lineitem on success, Event on error'}
+ }
+);
+
+sub create_lineitem {
+ my($self, $conn, $auth, $li) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+
+ if($li->picklist) {
+ my $picklist = $e->retrieve_acq_picklist($li->picklist)
+ or return $e->die_event;
+
+ if($picklist->owner != $e->requestor->id) {
+ return $e->die_event unless
+ $e->allowed('CREATE_PICKLIST', $picklist->org_unit, $picklist);
+ }
+
+ # indicate the picklist was updated
+ $picklist->edit_time('now');
+ $e->update_acq_picklist($picklist) or return $e->die_event;
+ }
+
+ if($li->purchase_order) {
+ my $po = $e->retrieve_acq_purchase_order($li->purchase_order)
+ or return $e->die_event;
+ return $e->die_event unless
+ $e->allowed('MANAGE_PROVIDER', $po->ordering_agency, $po);
+ }
+
+ $li->selector($e->requestor->id);
+ $e->create_acq_lineitem($li) or return $e->die_event;
+
+ $e->commit;
+ return $li->id;
+}
+
+__PACKAGE__->register_method(
+ method => 'create_po_assets',
+ api_name => 'open-ils.acq.purchase_order.assets.create',
+ signature => {
+ desc => q/Creates assets for each lineitem in the purchase order/,
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'The purchase order id', type => 'number'},
+ {desc => q/Options hash./}
+ ],
+ return => {desc => 'Streams a total versus completed counts object, event on error'}
+ }
+);
+
+sub create_po_assets {
+ my($self, $conn, $auth, $po_id, $options) = @_;
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->die_event unless $e->checkauth;
+
+ my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->event;
+ return $e->die_event unless
+ $e->allowed('CREATE_PURCHASE_ORDER', $po->ordering_agency);
+
+ my $li_ids = $e->search_acq_lineitem({purchase_order=>$po_id},{idlist=>1});
+ my $total = @$li_ids;
+ my $count = 0;
+
+ for my $li_id (@$li_ids) {
+ my $resp = create_lineitem_assets_impl($e, $auth, $li_id);
+ if($U->event_code($resp)) {
+ $e->rollback;
+ return $resp;
+ }
+ $conn->respond({total=>$count, progress=>++$count});
+ }
+
+ $po->edit_time('now');
+ $e->update_acq_purchase_order($po) or return $e->die_event;
+ $e->commit;
+
+ return {complete=>1};
+}
+
+__PACKAGE__->register_method(
+ method => 'create_lineitem_assets',
+ api_name => 'open-ils.acq.lineitem.assets.create',
+ signature => {
+ desc => q/Creates the bibliographic data, volume, and copies associated with a lineitem./,
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'The lineitem id', type => 'number'},
+ {desc => q/Options hash./}
+ ],
+ return => {desc => 'ID of newly created bib record, Event on error'}
+ }
+);
+
+sub create_lineitem_assets {
+ my($self, $conn, $auth, $li_id, $options) = @_;
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->die_event unless $e->checkauth;
+ my $resp = create_lineitem_assets_impl($e, $auth, $li_id, $options);
+ if($U->event_code($resp)) {
+ $e->rollback;
+ return $resp;
+ }
+ $e->commit;
+ return $resp;
+}
+
+sub create_lineitem_assets_impl {
+ my($e, $auth, $li_id, $options) = @_;
+ my $li = $e->retrieve_acq_lineitem([
+ $li_id,
+ { flesh => 1,
+ flesh_fields => {jub => ['purchase_order']}
+ }
+ ]) or return $e->die_event;
+
+ return OpenILS::Event->new('BAD_PARAMS') # make this perm-based, not owner-based
+ unless $li->purchase_order->owner == $e->requestor->id;
+
+ # -----------------------------------------------------------------
+ # first, create the bib record if necessary
+ # -----------------------------------------------------------------
+ unless($li->eg_bib_id) {
+ my $record = $U->simplereq(
+ 'open-ils.cat',
+ 'open-ils.cat.biblio.record.xml.import',
+ $auth, $li->marc, $li->source_label);
+
+ if($U->event_code($record)) {
+ $e->rollback;
+ return $record;
+ }
+
+ $li->eg_bib_id($record->id);
+ $e->update_acq_lineitem($li) or return $e->die_event;
+ }
+
+ my $li_details = $e->search_acq_lineitem_detail({lineitem => $li_id}, {idlist=>1});
+
+ # -----------------------------------------------------------------
+ # for each lineitem_detail, create the volume if necessary, create
+ # a copy, and link them all together.
+ # -----------------------------------------------------------------
+ my %volcache;
+ for my $li_detail_id (@{$li_details}) {
+
+ my $li_detail = $e->retrieve_acq_lineitem_detail($li_detail_id)
+ or return $e->die_event;
+
+ my $volume = $volcache{$li_detail->cn_label};
+ unless($volume and $volume->owning_lib == $li_detail->owning_lib) {
+ my $vol_id = $U->simplereq(
+ 'open-ils.cat',
+ 'open-ils.cat.call_number.find_or_create',
+ $auth, $li_detail->cn_label, $li->eg_bib_id, $li_detail->owning_lib);
+ $volume = $e->retrieve_asset_call_number($vol_id) or return $e->die_event;
+ $volcache{$vol_id} = $volume;
+ }
+
+ if($U->event_code($volume)) {
+ $e->rollback;
+ return $volume;
+ }
+
+ my $copy = Fieldmapper::asset::copy->new;
+ $copy->isnew(1);
+ $copy->loan_duration(2);
+ $copy->fine_level(2);
+ $copy->status(OILS_COPY_STATUS_ON_ORDER);
+ $copy->barcode($li_detail->barcode);
+ $copy->location($li_detail->location);
+ $copy->call_number($volume->id);
+ $copy->circ_lib($volume->owning_lib);
+
+ my $stat = $U->simplereq(
+ 'open-ils.cat',
+ 'open-ils.cat.asset.copy.fleshed.batch.update', $auth, [$copy]);
+
+ if($U->event_code($stat)) {
+ $e->rollback;
+ return $stat;
+ }
+
+ my $new_copy = $e->search_asset_copy({deleted=>'f', barcode=>$copy->barcode})->[0]
+ or return $e->die_event;
+
+ $li_detail->eg_copy_id($new_copy->id);
+ $e->update_acq_lineitem_detail($li_detail) or return $e->die_event;
+ }
+
+ return 1;
+}
+
+
+
+__PACKAGE__->register_method(
+ method => 'retrieve_lineitem',
+ api_name => 'open-ils.acq.lineitem.retrieve',
+ signature => {
+ desc => 'Retrieves a lineitem',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'lineitem ID to retrieve', type => 'number'},
+ {options => q/Hash of options, including
+ "flesh_attrs", which fleshes the attributes;
+ "flesh_li_details", which fleshes the order details objects/, type => 'hash'},
+ ],
+ return => {desc => 'lineitem object on success, Event on error'}
+ }
+);
+
+
+sub retrieve_lineitem {
+ my($self, $conn, $auth, $li_id, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+ $options ||= {};
+
+ # XXX finer grained perms...
+
+ my $li;
+
+ if($$options{flesh_attrs}) {
+ $li = $e->retrieve_acq_lineitem([
+ $li_id, {flesh => 1, flesh_fields => {jub => ['attributes']}}])
+ or return $e->event;
+ } else {
+ $li = $e->retrieve_acq_lineitem($li_id) or return $e->event;
+ }
+
+ if($$options{flesh_li_details}) {
+ my $ops = {
+ flesh => 1,
+ flesh_fields => {acqlid => []}
+ };
+ push(@{$ops->{flesh_fields}->{acqlid}}, 'fund') if $$options{flesh_fund};
+ push(@{$ops->{flesh_fields}->{acqlid}}, 'fund_debit') if $$options{flesh_fund_debit};
+ my $details = $e->search_acq_lineitem_detail([{lineitem => $li_id}, $ops]);
+ $li->lineitem_details($details);
+ $li->item_count(scalar(@$details));
+ } else {
+ my $details = $e->search_acq_lineitem_detail({lineitem => $li_id}, {idlist=>1});
+ $li->item_count(scalar(@$details));
+ }
+
+ if($li->picklist) {
+ my $picklist = $e->retrieve_acq_picklist($li->picklist)
+ or return $e->event;
+
+ if($picklist->owner != $e->requestor->id) {
+ return $e->event unless
+ $e->allowed('VIEW_PICKLIST', undef, $picklist);
+ }
+ }
+
+ $li->clear_marc if $$options{clear_marc};
+
+ return $li;
+}
+
+
+
+__PACKAGE__->register_method(
+ method => 'delete_lineitem',
+ api_name => 'open-ils.acq.lineitem.delete',
+ signature => {
+ desc => 'Deletes a lineitem',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'lineitem ID to delete', type => 'number'},
+ ],
+ return => {desc => '1 on success, Event on error'}
+ }
+);
+
+sub delete_lineitem {
+ my($self, $conn, $auth, $li_id) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ my $li = $e->retrieve_acq_lineitem($li_id)
+ or return $e->die_event;
+
+ # XXX check state
+
+ if($li->picklist) {
+ my $picklist = $e->retrieve_acq_picklist($li->picklist)
+ or return $e->die_event;
+ return OpenILS::Event->new('BAD_PARAMS')
+ if $picklist->owner != $e->requestor->id;
+ } else {
+ # check PO perms
+ }
+
+ # delete the attached lineitem_details
+ my $lid_ids = $e->search_acq_lineitem_detail(
+ {lineitem => $li_id}, {idlist=>1});
+
+ for my $lid_id (@$lid_ids) {
+ $e->delete_acq_lineitem_detail(
+ $e->retrieve_acq_lineitem_detail($lid_id))
+ or return $e->die_event;
+ }
+
+ $e->delete_acq_lineitem($li) or return $e->die_event;
+ $e->commit;
+ return 1;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'update_lineitem',
+ api_name => 'open-ils.acq.lineitem.update',
+ signature => {
+ desc => 'Update a lineitem',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'lineitem object update', type => 'object'}
+ ],
+ return => {desc => '1 on success, Event on error'}
+ }
+);
+
+sub update_lineitem {
+ my($self, $conn, $auth, $li) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ my $orig_li = $e->retrieve_acq_lineitem([
+ $li->id,
+ { flesh => 1, # grab the lineitem with picklist attached
+ flesh_fields => {jub => ['picklist', 'purchase_order']}
+ }
+ ]) or return $e->die_event;
+
+ # the marc may have been cleared on retrieval...
+ $li->marc($e->retrieve_acq_lineitem($li->id)->marc)
+ unless $li->marc;
+
+ $e->update_acq_lineitem($li) or return $e->die_event;
+ $e->commit;
+ return 1;
+}
+
+__PACKAGE__->register_method(
+ method => 'lineitem_search',
+ api_name => 'open-ils.acq.lineitem.search',
+ stream => 1,
+ signature => {
+ desc => 'Searches lineitems',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Search definition', type => 'object'},
+ {desc => 'Optoins hash. idlist=true', type => 'object'},
+ {desc => 'List of lineitems', type => 'object/number'},
+ ]
+ }
+);
+
+sub lineitem_search {
+ my($self, $conn, $auth, $search, $options) = @_;
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->event unless $e->checkauth;
+ return $e->event unless $e->allowed('CREATE_PICKLIST');
+ # XXX needs permissions consideration
+ my $lis = $e->search_acq_lineitem($search, {idlist=>1});
+ for my $li_id (@$lis) {
+ if($$options{idlist}) {
+ $conn->respond($li_id);
+ } else {
+ my $res = retrieve_lineitem($self, $conn, $auth, $li_id, $options);
+ $conn->respond($res) unless $U->event_code($res);
+ }
+ }
+ return undef;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'lineitem_search_ident',
+ api_name => 'open-ils.acq.lineitem.search.ident',
+ stream => 1,
+ signature => {
+ desc => 'Performs a search against lineitem_attrs where ident is true',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ { desc => q/Search definition. Options are:
+ attr_values : list of attribute values (required)
+ li_states : list of lineitem states
+ po_agencies : list of purchase order ordering agencies (org) ids
+ /,
+ type => 'object',
+ },
+ { desc => q/
+ Options hash. Options are:
+ idlist : if set, only return lineitem IDs
+ clear_marc : if set, strip the MARC xml from the lineitem before delivery
+ flesh_attrs : flesh lineitem attributes;
+ /,
+ type => 'object',
+ }
+ ]
+ }
+);
+
+my $LI_ATTR_SEARCH = {
+ select => {acqlia => ['lineitem']},
+ from => {
+ acqlia => {
+ acqliad => {
+ field => 'id',
+ fkey => 'definition'
+ },
+ jub => {
+ field => 'id',
+ fkey => 'lineitem',
+ join => {
+ acqpo => {
+ field => 'id',
+ fkey => 'purchase_order'
+ }
+ }
+ }
+ }
+ }
+};
+
+sub lineitem_search_ident {
+ my($self, $conn, $auth, $search, $options) = @_;
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->event unless $e->checkauth;
+ # XXX needs permissions consideration
+
+ return [] unless $search;
+ my $attr_values = $search->{attr_values};
+ my $li_states = $search->{li_states};
+ my $po_agencies = $search->{po_agencies}; # XXX if none, base it on perms
+
+ my $where_clause = {
+ '-or' => [],
+ '+acqlia' => {
+ '+acqliad' => {ident => 't'},
+ }
+ };
+
+ push(@{$where_clause->{'-or'}}, {attr_value => {ilike => "%$_%"}}) for @$attr_values;
+
+ $where_clause->{'+jub'} = {state => {in => $li_states}}
+ if $li_states and @$li_states;
+
+ $where_clause->{'+acqpo'} = {ordering_agency => $po_agencies}
+ if $po_agencies and @$po_agencies;
+
+ $LI_ATTR_SEARCH->{where} = $where_clause;
+
+ my $lis = $e->json_query($LI_ATTR_SEARCH);
+
+ for my $li_id_obj (@$lis) {
+ my $li_id = $li_id_obj->{lineitem};
+ if($$options{idlist}) {
+ $conn->respond($li_id);
+ } else {
+ my $li;
+ if($$options{flesh_attrs}) {
+ $li = $e->retrieve_acq_lineitem([
+ $li_id, {flesh => 1, flesh_fields => {jub => ['attributes']}}])
+ } else {
+ $li = $e->retrieve_acq_lineitem($li_id);
+ }
+ $li->clear_marc if $$options{clear_marc};
+ $conn->respond($li);
+ }
+ }
+ return undef;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'create_lineitem_detail',
+ api_name => 'open-ils.acq.lineitem_detail.create',
+ signature => {
+ desc => q/Creates a new purchase order line item detail.
+ Additionally creates the associated fund_debit/,
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'lineitem_detail to create', type => 'object'},
+ ],
+ return => {desc => 'The purchase order line item detail id, Event on failure'}
+ }
+);
+
+sub create_lineitem_detail {
+ my($self, $conn, $auth, $li_detail, $options) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+ $options ||= {};
+
+ my $li = $e->retrieve_acq_lineitem($li_detail->lineitem)
+ or return $e->die_event;
+
+ my $evt = update_li_edit_time($e, $li);
+ return $evt if $evt;
+
+ # XXX check lineitem provider perms
+
+ if($li_detail->fund) {
+ my $fund = $e->retrieve_acq_fund($li_detail->fund) or return $e->die_event;
+ return $e->die_event unless
+ $e->allowed('MANAGE_FUND', $fund->org, $fund);
+ }
+
+ $e->create_acq_lineitem_detail($li_detail) or return $e->die_event;
+
+ unless($li_detail->barcode) {
+ my $pfx = $U->ou_ancestor_setting_value($li_detail->owning_lib, 'acq.tmp_barcode_prefix') || 'ACQ';
+ $li_detail->barcode($pfx.$li_detail->id);
+ }
+ unless($li_detail->cn_label) {
+ my $pfx = $U->ou_ancestor_setting_value($li_detail->owning_lib, 'acq.tmp_callnumber_prefix') || 'ACQ';
+ $li_detail->cn_label($pfx.$li_detail->id);
+ }
+
+ if(my $loc = $U->ou_ancestor_setting_value($li_detail->owning_lib, 'acq.default_copy_location')) {
+ $li_detail->location($loc);
+ }
+
+ $e->update_acq_lineitem_detail($li_detail) or return $e->die_event;
+
+ $e->commit;
+ return $li_detail if $$options{return_obj};
+ return $li_detail->id
+}
+
+__PACKAGE__->register_method(
+ method => 'update_lineitem_detail',
+ api_name => 'open-ils.acq.lineitem_detail.update',
+ signature => {
+ desc => q/Updates a lineitem detail/,
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'lineitem_detail to update', type => 'object'},
+ ],
+ return => {desc => '1 on success, Event on failure'}
+ }
+);
+
+sub update_lineitem_detail {
+ my($self, $conn, $auth, $li_detail) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ if($li_detail->fund) {
+ my $fund = $e->retrieve_acq_fund($li_detail->fund) or return $e->die_event;
+ return $e->die_event unless
+ $e->allowed('MANAGE_FUND', $fund->org, $fund);
+ }
+
+ # XXX check lineitem perms
+
+ my $li = $e->retrieve_acq_lineitem($li_detail->lineitem)
+ or return $e->die_event;
+ my $evt = update_li_edit_time($e, $li);
+ return $evt if $evt;
+
+ $e->update_acq_lineitem_detail($li_detail) or return $e->die_event;
+ $e->commit;
+ return 1;
+}
+
+sub update_li_edit_time {
+ my ($e, $li) = @_;
+ # some lineitem edits are allowed after approval time...
+# return OpenILS::Event->new('ACQ_LINEITEM_APPROVED', payload => $li->id)
+# if $li->state eq 'approved';
+ $li->edit_time('now');
+ $e->update_acq_lineitem($li) or return $e->die_event;
+ return undef;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'delete_lineitem_detail',
+ api_name => 'open-ils.acq.lineitem_detail.delete',
+ signature => {
+ desc => q/Deletes a lineitem detail/,
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'lineitem_detail ID to delete', type => 'number'},
+ ],
+ return => {desc => '1 on success, Event on failure'}
+ }
+);
+
+sub delete_lineitem_detail {
+ my($self, $conn, $auth, $li_detail_id) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+ my $li_detail = $e->retrieve_acq_lineitem_detail([
+ $li_detail_id,
+ { flesh => 1,
+ flesh_fields => {acqlid => ['lineitem']}
+ }
+ ]) or return $e->die_event;
+
+ my $li = $li_detail->lineitem;
+
+ my $evt = update_li_edit_time($e, $li);
+ return $evt if $evt;
+
+ return OpenILS::Event->new('BAD_PARAMS') unless
+ $li->state =~ /new|approved/;
+
+ # XXX check lineitem perms
+
+ $e->delete_acq_lineitem_detail($li_detail) or return $e->die_event;
+ $e->commit;
+ return 1;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'retrieve_lineitem_detail',
+ api_name => 'open-ils.acq.lineitem_detail.retrieve',
+ signature => {
+ desc => q/Updates a lineitem detail/,
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'id of lineitem_detail to retrieve', type => 'number'},
+ ],
+ return => {desc => 'object on success, Event on failure'}
+ }
+);
+sub retrieve_lineitem_detail {
+ my($self, $conn, $auth, $li_detail_id) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+
+ my $li_detail = $e->retrieve_acq_lineitem_detail($li_detail_id)
+ or return $e->event;
+
+ if($li_detail->fund) {
+ my $fund = $e->retrieve_acq_fund($li_detail->fund) or return $e->event;
+ return $e->event unless
+ $e->allowed('MANAGE_FUND', $fund->org, $fund);
+ }
+
+ # XXX check lineitem perms
+ return $li_detail;
+}
+
+
+
+__PACKAGE__->register_method(
+ method => 'approve_lineitem',
+ api_name => 'open-ils.acq.lineitem.approve',
+ signature => {
+ desc => 'Mark a lineitem as approved',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'lineitem ID', type => 'number'}
+ ],
+ return => {desc => '1 on success, Event on error'}
+ }
+);
+sub approve_lineitem {
+ my($self, $conn, $auth, $li_id) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ # XXX perm checks for each lineitem detail
+
+ my $li = $e->retrieve_acq_lineitem($li_id)
+ or return $e->die_event;
+
+ return OpenILS::Event->new('ACQ_LINEITEM_APPROVED', payload => $li_id)
+ if $li->state eq 'approved';
+
+ my $details = $e->search_acq_lineitem_detail({lineitem => $li_id});
+ return OpenILS::Event->new('ACQ_LINEITEM_NO_COPIES', payload => $li_id)
+ unless scalar(@$details) > 0;
+
+ for my $detail (@$details) {
+ return OpenILS::Event->new('ACQ_LINEITEM_DETAIL_NO_FUND', payload => $detail->id)
+ unless $detail->fund;
+
+ return OpenILS::Event->new('ACQ_LINEITEM_DETAIL_NO_ORG', payload => $detail->id)
+ unless $detail->owning_lib;
+ }
+
+ $li->state('approved');
+ $li->edit_time('now');
+ $e->update_acq_lineitem($li) or return $e->die_event;
+
+ $e->commit;
+ return 1;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'receive_lineitem_detail',
+ api_name => 'open-ils.acq.lineitem_detail.receive',
+ signature => {
+ desc => 'Mark a lineitem_detail as received',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'lineitem detail ID', type => 'number'}
+ ],
+ return => {desc => '1 on success, Event on error'}
+ }
+);
+sub receive_lineitem_detail {
+ my($self, $conn, $auth, $lid_id) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+ my $resp = receive_lineitem_detail_impl($e, $lid_id);
+ if($resp) {$e->rollback; return $resp;}
+ $e->commit;
+ return 1;
+}
+
+sub receive_lineitem_detail_impl {
+ my($e, $lid_id) = @_;
+
+ my $lid = $e->retrieve_acq_lineitem_detail([
+ $lid_id,
+ { flesh => 1,
+ flesh_fields => {
+ acqlid => ['fund_debit']
+ }
+ }
+ ]) or return $e->die_event;
+
+ return OpenILS::Event->new(
+ 'ACQ_LINEITEM_DETAIL_RECEIVED') if $lid->recv_time;
+
+ $lid->recv_time('now');
+ $e->update_acq_lineitem_detail($lid) or return $e->die_event;
+
+ my $copy = $e->retrieve_asset_copy($lid->eg_copy_id)
+ or return $e->die_event;
+
+ $copy->status(OILS_COPY_STATUS_IN_PROCESS);
+ $copy->edit_date('now');
+ $copy->editor($e->requestor->id);
+ $e->update_asset_copy($copy) or return $e->die_event;
+
+ if($lid->fund_debit) {
+ $lid->fund_debit->encumbrance('f');
+ $e->update_acq_fund_debit($lid->fund_debit) or return $e->die_event;
+ }
+
+ # -------------------------------------------------------------
+ # if all of the lineitem details for this lineitem have
+ # been received, mark the lineitem as received
+ # -------------------------------------------------------------
+ my $non_recv = $e->search_acq_lineitem_detail(
+ {recv_time => undef, lineitem => $lid->lineitem}, {idlist=>1});
+
+ return undef if @$non_recv;
+
+ my $li = $e->retrieve_acq_lineitem($lid->lineitem);
+ $li->state('received');
+ $li->edit_time('now');
+ $e->update_acq_lineitem($li) or return $e->die_event;
+
+ # -------------------------------------------------------------
+ # if all of the lineitems for this PO are received,
+ # mark the PO as received
+ # -------------------------------------------------------------
+ my $non_recv_li = $e->search_acq_lineitem(
+ { purchase_order => $li->purchase_order,
+ state => {'!=' => 'received'}
+ }, {idlist=>1});
+
+ return undef if @$non_recv_li;
+
+ my $po = $e->retrieve_acq_purchase_order($li->purchase_order);
+ $po->state('received');
+ $po->edit_time('now');
+ $e->update_acq_purchase_order($po) or return $e->die_event;
+
+ return undef;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'set_lineitem_attr',
+ api_name => 'open-ils.acq.lineitem_usr_attr.set',
+ signature => {
+ desc => 'Sets a lineitem_usr_attr value',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Lineitem ID', type => 'number'},
+ {desc => 'Attr name', type => 'string'},
+ {desc => 'Attr value', type => 'string'}
+ ],
+ return => {desc => '1 on success, Event on error'}
+ }
+);
+
+__PACKAGE__->register_method(
+ method => 'set_lineitem_attr',
+ api_name => 'open-ils.acq.lineitem_local_attr.set',
+ signature => {
+ desc => 'Sets a lineitem_local_attr value',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Lineitem ID', type => 'number'},
+ {desc => 'Attr name', type => 'string'},
+ {desc => 'Attr value', type => 'string'}
+ ],
+ return => {desc => 'ID of the attr object on success, Event on error'}
+ }
+);
+
+
+sub set_lineitem_attr {
+ my($self, $conn, $auth, $li_id, $attr_name, $attr_value) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ # XXX perm
+
+ my $attr_type = $self->api_name =~ /local_attr/ ?
+ 'lineitem_local_attr_definition' : 'lineitem_usr_attr_definition';
+
+ my $attr = $e->search_acq_lineitem_attr({
+ lineitem => $li_id,
+ attr_type => $attr_type,
+ attr_name => $attr_name})->[0];
+
+ my $find = "search_acq_$attr_type";
+
+ if($attr) {
+ $attr->attr_value($attr_value);
+ $e->update_acq_lineitem_attr($attr) or return $e->die_event;
+ } else {
+ $attr = Fieldmapper::acq::lineitem_attr->new;
+ $attr->lineitem($li_id);
+ $attr->attr_type($attr_type);
+ $attr->attr_name($attr_name);
+ $attr->attr_value($attr_value);
+
+ my $attr_def_id = $e->$find({code => $attr_name}, {idlist=>1})->[0]
+ or return $e->die_event;
+ $attr->definition($attr_def_id);
+ $e->create_acq_lineitem_attr($attr) or return $e->die_event;
+ }
+
+ $e->commit;
+ return $attr->id;
+}
+
+__PACKAGE__->register_method(
+ method => 'get_lineitem_attr_defs',
+ api_name => 'open-ils.acq.lineitem_attr_definition.retrieve.all',
+ signature => {
+ desc => 'Retrieve lineitem attr definitions',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ ],
+ return => {desc => 'List of attr definitions'}
+ }
+);
+
+sub get_lineitem_attr_defs {
+ my($self, $conn, $auth) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ my %results;
+ for my $type (qw/generated marc local usr provider/) {
+ my $call = "retrieve_all_acq_lineitem_${type}_attr_definition";
+ $results{$type} = $e->$call;
+ }
+ return \%results;
+}
+
+
+1;
--- /dev/null
+package OpenILS::Application::Acq::Picklist;
+use base qw/OpenILS::Application/;
+use strict; use warnings;
+
+use OpenSRF::Utils::Logger qw(:logger);
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Utils::CStoreEditor q/:funcs/;
+use OpenILS::Const qw/:const/;
+use OpenSRF::Utils::SettingsClient;
+use OpenILS::Event;
+use OpenILS::Application::AppUtils;
+my $U = 'OpenILS::Application::AppUtils';
+
+
+__PACKAGE__->register_method(
+ method => 'create_picklist',
+ api_name => 'open-ils.acq.picklist.create',
+ signature => {
+ desc => 'Creates a new picklist',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Picklist object to create', type => 'object'}
+ ],
+ return => {desc => 'The ID of the new picklist'}
+ }
+);
+
+sub create_picklist {
+ my($self, $conn, $auth, $picklist) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+ $picklist->org_unit($e->requestor->ws_ou) unless $picklist->org_unit;
+ return $e->die_event unless $e->allowed('CREATE_PICKLIST', $picklist->org_unit);
+ return OpenILS::Event->new('BAD_PARAMS')
+ unless $e->requestor->id == $picklist->owner;
+ $e->create_acq_picklist($picklist) or return $e->die_event;
+ $e->commit;
+ return $picklist->id;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'update_picklist',
+ api_name => 'open-ils.acq.picklist.update',
+ signature => {
+ desc => 'Updates a new picklist',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Picklist object to update', type => 'object'}
+ ],
+ return => {desc => '1 on success, Event on error'}
+ }
+);
+
+sub update_picklist {
+ my($self, $conn, $auth, $picklist) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ # don't let them change the owner
+ my $o_picklist = $e->retrieve_acq_picklist($picklist->id)
+ or return $e->die_event;
+ if($o_picklist->owner != $e->requestor->id) {
+ return $e->die_event unless
+ $e->allowed('UPDATE_PICKLIST', $o_picklist->org_unit);
+ }
+ return OpenILS::Event->new('BAD_PARAMS') unless $o_picklist->org_unit == $picklist->org_unit;
+
+ $e->update_acq_picklist($picklist) or return $e->die_event;
+ $e->commit;
+ return 1;
+}
+
+__PACKAGE__->register_method(
+ method => 'retrieve_picklist',
+ api_name => 'open-ils.acq.picklist.retrieve',
+ signature => {
+ desc => 'Retrieves a picklist',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Picklist ID to retrieve', type => 'number'},
+ {desc => 'Options hash, including "flesh_lineitem_count" to get the count of attached entries', type => 'hash'},
+ ],
+ return => {desc => 'Picklist object on success, Event on error'}
+ }
+);
+
+sub retrieve_picklist {
+ my($self, $conn, $auth, $picklist_id, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+
+ my $picklist = $e->retrieve_acq_picklist($picklist_id)
+ or return $e->event;
+
+ $picklist->entry_count(retrieve_lineitem_count($e, $picklist_id))
+ if $$options{flesh_lineitem_count};
+
+ if($e->requestor->id != $picklist->owner) {
+ return $e->event unless
+ $e->allowed('VIEW_PICKLIST', $picklist->org_unit, $picklist);
+ }
+
+ $picklist->owner($e->retrieve_actor_user($picklist->owner)->usrname)
+ if($$options{flesh_username});
+
+ return $picklist;
+}
+
+
+# Returns the number of entries associated with this picklist
+sub retrieve_lineitem_count {
+ my($e, $picklist_id) = @_;
+ my $count = $e->json_query({
+ select => {
+ jub => [{transform => 'count', column => 'id', alias => 'count'}]
+ },
+ from => 'jub',
+ where => {picklist => $picklist_id}}
+ );
+ return $count->[0]->{count};
+}
+
+
+
+__PACKAGE__->register_method(
+ method => 'retrieve_picklist_name',
+ api_name => 'open-ils.acq.picklist.name.retrieve',
+ signature => {
+ desc => 'Retrieves a picklist by name. Owner is implied by the caller',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Picklist name to retrieve', type => 'strin'},
+ ],
+ return => {desc => 'Picklist object on success, null on not found'}
+ }
+);
+
+sub retrieve_picklist_name {
+ my($self, $conn, $auth, $name) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ my $picklist = $e->search_acq_picklist(
+ {name => $name, owner => $e->requestor->id})->[0];
+ if($e->requestor->id != $picklist->owner) {
+ return $e->event unless
+ $e->allowed('VIEW_PICKLIST', $picklist->org_unit, $picklist);
+ }
+ return $picklist;
+}
+
+
+
+__PACKAGE__->register_method(
+ method => 'retrieve_user_picklist',
+ api_name => 'open-ils.acq.picklist.user.retrieve',
+ stream => 1,
+ signature => {
+ desc => 'Retrieves a user\'s picklists',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Options, including "idlist", whch forces the return
+ of a list of IDs instead of objects', type => 'hash'},
+ ],
+ return => {desc => 'Picklist object on success, Event on error'}
+ }
+);
+
+sub retrieve_user_picklist {
+ my($self, $conn, $auth, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ # don't grab the PL with name == "", because that is the designated temporary picklist
+ my $list = $e->search_acq_picklist(
+ {owner=>$e->requestor->id, name=>{'!='=>''}},
+ {idlist=>1}
+ );
+
+ for my $id (@$list) {
+ if($$options{idlist}) {
+ $conn->respond($id);
+ } else {
+ my $pl = $e->retrieve_acq_picklist($id);
+ $pl->entry_count(retrieve_lineitem_count($e, $id)) if $$options{flesh_lineitem_count};
+ $pl->owner($e->retrieve_actor_user($pl->owner)->usrname) if $$options{flesh_username};
+ $conn->respond($pl);
+ }
+ }
+
+ return undef;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'retrieve_all_user_picklist',
+ api_name => 'open-ils.acq.picklist.user.all.retrieve',
+ stream => 1,
+ signature => {
+ desc => 'Retrieves all of the picklists a user is allowed to see',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Options, including "idlist", whch forces the return
+ of a list of IDs instead of objects', type => 'hash'},
+ ],
+ return => {desc => 'Picklist objects on success, Event on error'}
+ }
+);
+
+sub retrieve_all_user_picklist {
+ my($self, $conn, $auth, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+
+ my $my_list = $e->search_acq_picklist(
+ {owner=>$e->requestor->id, name=>{'!='=>''}}, {idlist=>1});
+
+ my $picklist_ids = $e->objects_allowed('VIEW_PICKLIST', 'acqpl');
+ my $p_orgs = $U->find_highest_work_orgs($e, 'VIEW_PICKLIST', {descendants =>1});
+ my $picklist_ids_2 = $e->search_acq_picklist(
+ {name=>{'!='=>''}, org_unit => $p_orgs}, {idlist=>1});
+
+ return undef unless @$my_list or @$picklist_ids or @$picklist_ids_2;
+
+ my @list = (@$my_list, @$picklist_ids, @$picklist_ids_2);
+ my %dedup;
+ $dedup{$_} = 1 for @list;
+ @list = keys %dedup;
+
+ return \@list if $$options{idlist};
+
+ for my $pl (@list) {
+ my $picklist = $e->retrieve_acq_picklist($pl) or return $e->event;
+ $picklist->entry_count(retrieve_lineitem_count($e, $picklist->id))
+ if($$options{flesh_lineitem_count});
+ $picklist->owner($e->retrieve_actor_user($picklist->owner)->usrname)
+ if $$options{flesh_username};
+ $conn->respond($picklist);
+ }
+
+ return undef;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'delete_picklist',
+ api_name => 'open-ils.acq.picklist.delete',
+ signature => {
+ desc => q/Deletes a picklist. It also deletes any lineitems in the "new" state.
+ Other attached lineitems are detached'/,
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Picklist ID to delete', type => 'number'}
+ ],
+ return => {desc => '1 on success, Event on error'}
+ }
+);
+
+sub delete_picklist {
+ my($self, $conn, $auth, $picklist_id) = @_;
+ my $e = new_editor(xact=>1, authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ my $picklist = $e->retrieve_acq_picklist($picklist_id)
+ or return $e->die_event;
+ # don't let anyone delete someone else's picklist
+ if($picklist->owner != $e->requestor->id) {
+ return $e->die_event unless
+ $e->allowed('DELETE_PICKLIST', $picklist->org_unit, $picklist);
+ }
+
+ # delete all 'new' lineitems
+ my $lis = $e->search_acq_lineitem({picklist => $picklist->id, state => 'new'});
+ for my $li (@$lis) {
+ $e->delete_acq_lineitem($li) or return $e->die_event;
+ }
+
+ # detach all non-'new' lineitems
+ $lis = $e->search_acq_lineitem({picklist => $picklist->id, state => {'!=' => 'new'}});
+ for my $li (@$lis) {
+ $li->clear_picklist;
+ $e->update_acq_lineitem($li) or return $e->die_event;
+ }
+
+ # remove any picklist-specific object perms
+ my $ops = $e->search_permission_usr_object_perm_map({object_type => 'acqpl', object_id => $picklist->id});
+ for my $op (@$ops) {
+ $e->delete_usr_object_perm_map($op) or return $e->die_event;
+ }
+
+
+ $e->delete_acq_picklist($picklist) or return $e->die_event;
+ $e->commit;
+ return 1;
+}
+
+__PACKAGE__->register_method(
+ method => 'retrieve_pl_lineitem',
+ api_name => 'open-ils.acq.lineitem.picklist.retrieve',
+ stream => 1,
+ signature => {
+ desc => 'Retrieves lineitem objects according to picklist',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Picklist ID whose entries to retrieve', type => 'number'},
+ {desc => q/Options, including
+ "sort_attr", which defines the attribute to sort on;
+ "sort_attr_type", which defines the attribute type sort on;
+ "sort_dir", which defines the sort order between "asc" and "desc";
+ "limit", retrieval limit;
+ "offset", retrieval offset;
+ "idlist", return a list of IDs instead of objects
+ "flesh_attrs", additionaly return the list of flattened attributes
+ "clear_marc", discards the raw MARC data to reduce data size
+ /,
+ type => 'hash'}
+ ],
+ return => {desc => 'Array of lineitem objects or IDs, on success, Event on error'}
+ }
+);
+
+
+my $PL_ENTRY_JSON_QUERY = {
+ select => {jub => ["id"], "acqlia" => ["attr_value"]},
+ "from" => {
+ "jub" => {
+ "acqlia" => {
+ "fkey" => "id",
+ "field" => "lineitem",
+ "type" => "left",
+ "filter" => {
+ "attr_type" => "lineitem_marc_attr_definition",
+ "attr_name" => "author"
+ }
+ }
+ }
+ },
+ "order_by" => {"acqlia" => {"attr_value" => {"direction"=>"asc"}}},
+ "limit" => 10,
+ "where" => {"+jub" => {"picklist"=>2}},
+ "offset" => 0
+};
+
+sub retrieve_pl_lineitem {
+ my($self, $conn, $auth, $picklist_id, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+
+ # collect the retrieval options
+ my $sort_attr = $$options{sort_attr} || 'title';
+ my $sort_attr_type = $$options{sort_attr_type} || 'lineitem_marc_attr_definition';
+ my $sort_dir = $$options{sort_dir} || 'asc';
+ my $limit = $$options{limit} || 10;
+ my $offset = $$options{offset} || 0;
+
+ $PL_ENTRY_JSON_QUERY->{where}->{'+jub'}->{picklist} = $picklist_id;
+ $PL_ENTRY_JSON_QUERY->{from}->{jub}->{acqlia}->{filter}->{attr_name} = $sort_attr;
+ $PL_ENTRY_JSON_QUERY->{from}->{jub}->{acqlia}->{filter}->{attr_type} = $sort_attr_type;
+ $PL_ENTRY_JSON_QUERY->{order_by}->{acqlia}->{attr_value}->{direction} = $sort_dir;
+ $PL_ENTRY_JSON_QUERY->{limit} = $limit;
+ $PL_ENTRY_JSON_QUERY->{offset} = $offset;
+
+ my $entries = $e->json_query($PL_ENTRY_JSON_QUERY);
+
+ my @ids;
+ push(@ids, $_->{id}) for @$entries;
+
+ for my $id (@ids) {
+ if($$options{idlist}) {
+ $conn->respond($id);
+ next;
+ }
+
+ my $entry;
+ my $flesh = ($$options{flesh_attrs}) ?
+ {flesh => 1, flesh_fields => {jub => ['attributes']}} : {};
+
+ $entry = $e->retrieve_acq_lineitem([$id, $flesh]);
+ my $details = $e->search_acq_lineitem_detail({lineitem => $id}, {idlist=>1});
+ $entry->item_count(scalar(@$details));
+ $entry->clear_marc if $$options{clear_marc};
+ $conn->respond($entry);
+ }
+
+ return undef;
+}
+
+=head comment
+request open-ils.cstore open-ils.cstore.json_query.atomic {"select":{"jub":[{"transform":"count", "attregate":1, "column":"id","alias":"count"}]}, "from":"jub","where":{"picklist":1}}
+=cut
+
+__PACKAGE__->register_method(
+ method => 'zsearch',
+ api_name => 'open-ils.acq.picklist.search.z3950',
+ stream => 1,
+ signature => {
+ desc => 'Performs a z3950 federated search and creates a picklist and associated lineitems',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Search definition', type => 'object'},
+ {desc => 'Picklist name, optional', type => 'string'},
+ ]
+ }
+);
+
+sub zsearch {
+ my($self, $conn, $auth, $search, $name) = @_;
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->die_event unless $e->checkauth;
+ return $e->die_event unless $e->allowed('CREATE_PICKLIST');
+
+ $search->{limit} ||= 10;
+
+ $name ||= '';
+ my $picklist = $e->search_acq_picklist({owner=>$e->requestor->id, name=>$name})->[0];
+ if($name eq '' and $picklist) {
+ my $evt = delete_picklist($self, $conn, $auth, $picklist->id);
+ return $evt unless $evt == 1;
+ $picklist = undef;
+ }
+
+ unless($picklist) {
+ $picklist = Fieldmapper::acq::picklist->new;
+ $picklist->owner($e->requestor->id);
+ $picklist->name($name);
+ $picklist->org_unit($e->requestor->ws_ou);
+ $e->create_acq_picklist($picklist) or return $e->die_event;
+ }
+
+ my $ses = OpenSRF::AppSession->create('open-ils.search');
+ my $req = $ses->request('open-ils.search.z3950.search_class', $auth, $search);
+
+ while(my $resp = $req->recv(timeout=>60)) {
+
+ my $result = $resp->content;
+ #use Data::Dumper;
+ #$logger->info("results = ".Dumper($resp));
+ my $count = $result->{count};
+ my $total = (($count < $search->{limit}) ? $count : $search->{limit})+1;
+ my $ctr = 0;
+ $conn->respond({total=>$total, progress=>++$ctr});
+
+ for my $rec (@{$result->{records}}) {
+ my $li = Fieldmapper::acq::lineitem->new;
+ $li->picklist($picklist->id);
+ $li->source_label($result->{service});
+ $li->selector($e->requestor->id);
+ $li->marc($rec->{marcxml});
+ $li->eg_bib_id($rec->{bibid}) if $rec->{bibid};
+ $e->create_acq_lineitem($li) or return $e->die_event;
+ $conn->respond({total=>$total, progress=>++$ctr});
+ }
+ }
+
+ $e->commit;
+ return {complete=>1, picklist_id=>$picklist->id};
+}
+
+
+
+1;
--- /dev/null
+package OpenILS::Application::Acq::Provider;
+use base qw/OpenILS::Application/;
+use strict; use warnings;
+
+use OpenILS::Event;
+use OpenILS::Const qw/:const/;
+use OpenSRF::Utils::Logger qw(:logger);
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Utils::CStoreEditor q/:funcs/;
+use OpenSRF::Utils::SettingsClient;
+use OpenILS::Application::AppUtils;
+
+my $U = 'OpenILS::Application::AppUtils';
+
+__PACKAGE__->register_method(
+ method => 'create_provider',
+ api_name => 'open-ils.acq.provider.create',
+ signature => {
+ desc => 'Creates a new provider',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'provider object to create', type => 'object'}
+ ],
+ return => {desc => 'The ID of the new provider'}
+ }
+);
+
+sub create_provider {
+ my($self, $conn, $auth, $provider) = @_;
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->die_event unless $e->checkauth;
+ return $e->die_event unless $e->allowed('ADMIN_PROVIDER', $provider->owner);
+ $e->create_acq_provider($provider) or return $e->die_event;
+ $e->commit;
+ return $provider->id;
+}
+
+
+
+__PACKAGE__->register_method(
+ method => 'retrieve_provider',
+ api_name => 'open-ils.acq.provider.retrieve',
+ signature => {
+ desc => 'Retrieves a new provider',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'provider ID', type => 'number'}
+ ],
+ return => {desc => 'The provider object on success, Event on failure'}
+ }
+);
+
+sub retrieve_provider {
+ my($self, $conn, $auth, $provider_id) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ my $provider = $e->retrieve_acq_provider($provider_id) or return $e->event;
+ return $e->event unless $e->allowed(
+ ['ADMIN_PROVIDER', 'MANAGE_PROVIDER', 'VIEW_PROVIDER'], $provider->owner, $provider);
+ return $provider;
+}
+
+
+__PACKAGE__->register_method(
+ method => 'retrieve_org_providers',
+ api_name => 'open-ils.acq.provider.org.retrieve',
+ stream => 1,
+ signature => {
+ desc => 'Retrieves all the providers associated with an org unit that the requestor has access to see',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'List of org Unit IDs. If no IDs are provided, this method returns the
+ full set of funding sources this user has permission to view', type => 'number'},
+ {desc => q/Limiting permission. this permission is used find the work-org tree from which
+ the list of orgs is generated if no org ids are provided.
+ The default is ADMIN_PROVIDER/, type => 'string'},
+ ],
+ return => {desc => 'The provider objects on success, empty array otherwise'}
+ }
+);
+
+sub retrieve_org_providers {
+ my($self, $conn, $auth, $org_id_list, $options) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+
+ my $limit_perm = ($$options{limit_perm}) ? $$options{limit_perm} : 'ADMIN_PROVIDER';
+
+ return OpenILS::Event->new('BAD_PARAMS')
+ unless $limit_perm =~ /(ADMIN|MANAGE|VIEW)_PROVIDER/;
+
+ my $org_ids = ($org_id_list and @$org_id_list) ? $org_id_list :
+ $U->find_highest_work_orgs($e, $limit_perm, {descendants =>1});
+
+ return [] unless @$org_ids;
+ $conn->respond($_) for @{$e->search_acq_provider({owner => $org_ids})};
+
+ return undef;
+}
+
+__PACKAGE__->register_method(
+ method => 'retrieve_provider_attr_def',
+ api_name => 'open-ils.acq.lineitem_provider_attr_definition.provider.retrieve',
+ stream => 1,
+ signature => {
+ desc => 'Retrieves all of the lineitem_provider_attr_definition for a given provider',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Provider ID', type => 'number'}
+ ],
+ return => {desc => 'Streams a of lineitem_provider_attr_definition objects'}
+ }
+);
+
+sub retrieve_provider_attr_def {
+ my($self, $conn, $auth, $prov_id) = @_;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
+ my $provider = $e->retrieve_acq_provider($prov_id)
+ or return $e->event;
+ return $e->event unless $e->allowed('ADMIN_PROVIDER', $provider->owner);
+ for my $id (@{$e->search_acq_lineitem_provider_attr_definition({provider=>$prov_id},{idlist=>1})}) {
+ $conn->respond($e->retrieve_acq_lineitem_provider_attr_definition($id));
+ }
+
+ return undef;
+}
+
+__PACKAGE__->register_method(
+ method => 'create_provider_attr_def',
+ api_name => 'open-ils.acq.lineitem_provider_attr_definition.create',
+ signature => {
+ desc => 'Retrieves all of the lineitem_provider_attr_definition for a given provider',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Provider ID', type => 'number'}
+ ],
+ return => {desc => 'Streams a of lineitem_provider_attr_definition objects'}
+ }
+);
+
+sub create_provider_attr_def {
+ my($self, $conn, $auth, $attr_def) = @_;
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->die_event unless $e->checkauth;
+ my $provider = $e->retrieve_acq_provider($attr_def->provider)
+ or return $e->die_event;
+ return $e->die_event unless $e->allowed('ADMIN_PROVIDER', $provider->owner);
+ $e->create_acq_lineitem_provider_attr_definition($attr_def)
+ or return $e->die_event;
+ $e->commit;
+ return $attr_def->id;
+}
+
+__PACKAGE__->register_method(
+ method => 'delete_provider_attr_def',
+ api_name => 'open-ils.acq.lineitem_provider_attr_definition.delete',
+ signature => {
+ desc => 'Deletes a lineitem_provider_attr_definition',
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'ID', type => 'number'}
+ ],
+ return => {desc => '1 on success, event on failure'}
+ }
+);
+
+sub delete_provider_attr_def {
+ my($self, $conn, $auth, $id) = @_;
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->die_event unless $e->checkauth;
+ my $attr_def = $e->retrieve_acq_lineitem_provider_attr_definition($id)
+ or return $e->die_event;
+ my $provider = $e->retrieve_acq_provider($attr_def->provider)
+ or return $e->die_event;
+ return $e->die_event unless $e->allowed('ADMIN_PROVIDER', $provider->owner);
+ $e->delete_acq_lineitem_provider_attr_definition($attr_def)
+ or return $e->die_event;
+ $e->commit;
+ return 1;
+}
+
+1;
econst OILS_BILLING_TYPE_RENTAL => 'System: Rental';
econst OILS_BILLING_NOTE_SYSTEM => 'SYSTEM GENERATED';
+econst OILS_ACQ_DEBIT_TYPE_PURCHASE => 'purchase';
+econst OILS_ACQ_DEBIT_TYPE_TRANSFER => 'xfer';
+
# ---------------------------------------------------------------------
--- /dev/null
+package MFHD;
+use strict;
+use integer;
+use Carp;
+
+use MARC::Record;
+use MFHD::Caption;
+use MFHD::Holding;
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ my $rec = shift;
+
+ $self->{CAPTIONS} = {};
+ foreach my $caption ($rec->field('853')) {
+ my $cap_id;
+ $cap_id = $caption->subfield('8') || '0';
+ if (exists $self->{CAPTIONS}->{$cap_id}) {
+ carp "Multiple unlabelled MFHD captions";
+ }
+ $self->{CAPTIONS}->{$cap_id} = new MFHD::Caption($caption);
+ }
+
+ $self->{HOLDINGS} = {};
+ foreach my $holding ($rec->field('863')) {
+ my $linkage;
+ my ($link_id, $seqno);
+
+ $linkage = $holding->subfield('8');
+ ($link_id, $seqno) = split(/\./, $linkage);
+
+ if (!exists $self->{HOLDINGS}->{$link_id}) {
+ $self->{HOLDINGS}->{$link_id} = {};
+ }
+ $self->{HOLDINGS}->{$link_id}->{$seqno} =
+ new MFHD::Holding($seqno, $holding, $self->{CAPTIONS}->{$link_id});
+ }
+
+ bless ($self, $class);
+ return $self;
+}
+
+sub captions {
+ my $self = shift;
+
+ return sort keys %{$self->{CAPTIONS}}
+}
+
+sub holdings {
+ my $self = shift;
+ my $capid = shift;
+
+ return sort {$a->{SEQNO} cmp $b->{SEQNO}} values %{$self->{HOLDINGS}->{$capid}};
+}
+
+1;
--- /dev/null
+package MFHD::Caption;
+use strict;
+use integer;
+use Carp;
+
+use MARC::Record;
+
+sub new
+{
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $caption = shift;
+ my $self = {};
+ my $last_enum = undef;
+
+ $self->{CAPTION} = $caption;
+ $self->{ENUMS} = {};
+ $self->{CHRONS} = {};
+ $self->{PATTERN} = {};
+ $self->{COPY} = undef;
+ $self->{UNIT} = undef;
+
+ foreach my $subfield ($caption->subfields) {
+ my ($key, $val) = @$subfield;
+ if ($key eq '8') {
+ $self->{LINK} = $val;
+ } elsif ($key =~ /[a-h]/) {
+ # Enumeration Captions
+ $self->{ENUMS}->{$key} = {CAPTION => $val,
+ COUNT => undef,
+ RESTART => undef};
+ if ($key =~ /[ag]/) {
+ $last_enum = undef;
+ } else {
+ $last_enum = $key;
+ }
+ } elsif ($key =~ /[i-m]/) {
+ # Chronology captions
+ $self->{CHRONS}->{$key} = $val;
+ } elsif ($key eq 'u') {
+ # Bib units per next higher enumeration level
+ carp('$u specified for top-level enumeration')
+ unless defined($last_enum);
+ $self->{ENUMS}->{$last_enum}->{COUNT} = $val;
+ } elsif ($key eq 'v') {
+ carp '$v specified for top-level enumeration'
+ unless defined($last_enum);
+ $self->{ENUMS}->{$last_enum}->{RESTART} = ($val eq 'r');
+ } elsif ($key =~ /[npw-z]/) {
+ # Publication Pattern ('o' == type of unit, 'q'..'t' undefined)
+ $self->{PATTERN}->{$key} = $val;
+ } elsif ($key eq 'o') {
+ # Type of unit
+ $self->{UNIT} = $val;
+ } elsif ($key eq 't') {
+ $self->{COPY} = $val;
+ } else {
+ carp "Unknown caption subfield '$key'";
+ }
+ }
+
+ bless ($self, $class);
+ return $self;
+}
+
+sub caption {
+ my $self = shift;
+ my $key;
+
+ if (@_) {
+ $key = shift;
+ return $self->{ENUMS}->{$key}->{CAPTION};
+ } else {
+ return $self->{CAPTION};
+ }
+}
+
+1;
--- /dev/null
+package MFHD::Holding;
+use strict;
+use integer;
+use Carp;
+
+use MARC::Record;
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $seqno = shift;
+ my $holding = shift;
+ my $caption = shift;
+ my $self = {};
+ my $last_enum = undef;
+
+ $self->{SEQNO} = $seqno;
+ $self->{HOLDING} = $holding;
+ $self->{CAPTION} = $caption;
+ $self->{ENUMS} = {};
+ $self->{CHRON} = {};
+ $self->{DESCR} = {};
+ $self->{COPY} = undef;
+ $self->{BREAK} = undef;
+ $self->{NOTES} = {};
+ $self->{COPYRIGHT} = [];
+
+ foreach my $subfield ($holding->subfields) {
+ my ($key, $val) = @$subfield;
+ if ($key =~ /[a-h]/) {
+ # Enumeration details of holdings
+ $self->{ENUMS}->{$key} = {HOLDINGS => $val,
+ UNIT => undef,};
+ $last_enum = $key;
+ } elsif ($key =~ /[i-m]/) {
+ $self->{CHRON}->{$key} = $val;
+ if (!exists $caption->{CHRONS}->{$key}) {
+ carp "Holding specified enumeration level '$key' not included in caption $caption->{LINK}";
+ }
+ } elsif ($key eq 'o') {
+ carp '$o specified prior to first enumeration'
+ unless defined($last_enum);
+ $self->{ENUMS}->{$last_enum}->{UNIT} = $val;
+ $last_enum = undef;
+ } elsif ($key =~ /[npq]/) {
+ $self->{DESCR}->{$key} = $val;
+ } elsif ($key eq 's') {
+ push @{$self->{COPYRIGHT}}, $val;
+ } elsif ($key eq 't') {
+ $self->{COPY} = $val;
+ } elsif ($key eq 'w') {
+ carp "Unrecognized break indicator '$val'"
+ unless $val =~ /^[gn]$/;
+ $self->{BREAK} = $val;
+ }
+ }
+
+ bless ($self, $class);
+ return $self;
+}
+
+sub format {
+ my $self = shift;
+ my $caption = $self->{CAPTION};
+ my $str = "";
+
+ foreach my $key ('a'..'f') {
+ last if !exists $caption->{ENUMS}->{$key};
+# printf("fmt %s: '%s'\n", $key, $caption->caption($key));
+
+ $str .= ($key eq 'a' ? "" : ':') . $caption->caption($key) . $self->{ENUMS}->{$key}->{HOLDINGS};
+ }
+
+ if (exists $caption->{ENUMS}->{'g'}) {
+ # There's at least one level of alternative enumeration
+ $str .= ' ';
+ foreach my $key ('g', 'h') {
+ $str .= ($key eq 'g' ? '' : ':') . $caption->enum($key) . $self->{ENUMS}->{$key}->{HOLDINGS};
+ }
+ }
+
+ return $str;
+}
+
+sub next {
+ my $self = shift;
+ my $caption = $self->{CAPTION};
+}
+
+1;
--- /dev/null
+#!/usr/bin/perl -w
+use strict;
+use Date::Manip;
+
+# Parse MFHD patterns for http://www.loc.gov/marc/holdings/hd853855.html
+
+# Primary goal:
+# Expected input: a chunk of MFHD, a start date, and # of issuances to project
+# Expected output: a set of issuances projected forward from the start date,
+# with issue/volume/dates/captions conforming to what the MFHD actually says
+
+# The thought had been to use Date::Manip to generate the actual dates for
+# each issuance, like:
+#
+# # To find the 2nd Tuesday of every month
+# @date = ParseRecur("0:1*2:2:0:0:0",$base,$start,$stop);
+
+# Secondary goal: generate automatic summary holdings
+# (a la http://www.loc.gov/marc/holdings/hd863865.html)
+
+# Compressability comes from first indicator
+sub parse_compressability {
+ my $c = shift || return undef;
+
+ my %compressability = (
+ '0' => 'Cannot compress or expand',
+ '1' => 'Can compress but cannot expand',
+ '2' => 'Can compress or expand',
+ '3' => 'Unknown',
+ '#' => 'Undefined'
+ );
+
+ if (exists $compressability{$c}) {
+ return $compressability{$c};
+ }
+ # 'Unknown compressability indicator - expected one of (0,1,2,3,#)';
+ return undef;
+}
+
+# Caption evaluation comes from second indicator
+sub caption_evaluation {
+ my $ce = shift || return undef;
+
+ my %caption_evaluation = (
+ '0' => 'Captions verified; all levels present',
+ '1' => 'Captions verified; all levels may not be present',
+ '2' => 'Captions unverified; all levels present',
+ '3' => 'Captions unverified; all levels may not be present',
+ '#' => 'Undefined',
+ );
+
+ if (exists $caption_evaluation{$ce}) {
+ return $caption_evaluation{$ce};
+ }
+ # 'Unknown caption evaluation indicator - expected one of (0,1,2,3,#)';
+ return undef;
+}
+
+# Start with frequency ($w)
+# then overlay number of pieces of issuance ($p)
+# then regularity pattern ($y)
+my %frequency = (
+ 'a' => 'annual',
+ 'b' => 'bimonthly',
+ 'c' => 'semiweekly',
+ 'd' => 'daily',
+ 'e' => 'biweekly',
+ 'f' => 'semiannual',
+ 'g' => 'biennial',
+ 'h' => 'triennial',
+ 'i' => 'three times a week',
+ 'j' => 'three times a month',
+ 'k' => 'continuously updated',
+ 'm' => 'monthly',
+ 'q' => 'quarterly',
+ 's' => 'semimonthly',
+ 't' => 'three times a year',
+ 'w' => 'weekly',
+ 'x' => 'completely irregular',
+);
+
+sub parse_frequency {
+ my $freq = shift || return undef;
+
+ if ($freq =~ m/^\d+$/) {
+ return "$freq times a year";
+ } elsif (exists $frequency{$freq}) {
+ return $frequency{$freq};
+ }
+ # unrecognized frequency specification
+ return undef;
+}
+
+# $x - Point at which the highest level increments or changes
+# Interpretation of two-digit numbers in the 01-12 range depends on the publishing frequency
+# More than one change can be passed in the subfield and are delimited by commas
+sub chronology_change {
+ my $chronology_change = shift || return undef;
+ my @c_changes = split /,/, $chronology_change;
+ foreach my $change (@c_changes) {
+ if ($change == 21) {
+
+ }
+ }
+ return undef;
+}
+
+# Publication code : first character in regularity pattern ($y)
+sub parse_publication_code {
+ my $c = shift || return undef;
+
+ my %publication_code = (
+ 'c' => 'combined',
+ 'o' => 'omitted',
+ 'p' => 'published',
+ '#' => 'undefined',
+ );
+
+ if (exists $publication_code{$c}) {
+ return $publication_code{$c};
+ }
+ return undef;
+}
+
+# Chronology code : part of regularity pattern ($y)
+sub parse_chronology_code {
+ my $c = shift || return undef;
+
+ my %chronology_code = (
+ 'd' => 'Day',
+ 'm' => 'Month',
+ 's' => 'Season',
+ 'w' => 'Week',
+ 'y' => 'Year',
+ 'e' => 'Enumeration',
+ );
+
+ if (exists $chronology_code{$c}) {
+ return $chronology_code{$c};
+ }
+ return undef;
+}
+
+sub parse_regularity_pattern {
+ my $pattern = shift;
+ my ($pc, $cc, $cd) = $pattern =~ m{^(\w)(\w)(.+)$};
+
+ my $pub_code = parse_publication_code($pc);
+ my $chron_code = parse_chronology_code($cc);
+ my $chron_def = parse_chronology_definition($cd);
+
+ return ($pub_code, $chron_code, $chron_def);
+}
+
+sub parse_chronology_definition {
+ my $chron_def = shift || return undef;
+ # Well, this is where it starts to get hard, doesn't it?
+ return $chron_def;
+}
+
+print parse_regularity_pattern("cddd");
+print "\n";
+print parse_regularity_pattern("38dd");
+print "\n";
+
+1;
+
+# :vim:noet:ts=4:sw=4:
--- /dev/null
+package OpenILS::WWW::EGWeb;
+use strict; use warnings;
+use Template;
+use XML::Simple;
+use File::stat;
+use Apache2::Const -compile => qw(OK DECLINED HTTP_INTERNAL_SERVER_ERROR);
+use Apache2::Log;
+
+use constant OILS_HTTP_COOKIE_SKIN => 'oils:skin';
+use constant OILS_HTTP_COOKIE_THEME => 'oils:theme';
+use constant OILS_HTTP_COOKIE_LOCALE => 'oils:locale';
+
+my $web_config;
+my $web_config_file;
+my $web_config_edit_time;
+
+sub import {
+ my $self = shift;
+ $web_config_file = shift;
+ unless(-r $web_config_file) {
+ warn "Invalid web config $web_config_file";
+ return;
+ }
+ check_web_config();
+}
+
+
+sub handler {
+ my $r = shift;
+ check_web_config($r); # option to disable this
+ my $ctx = load_context($r);
+ my $base = $ctx->{base_uri};
+ my($template, $page_args) = find_template($r, $base);
+ return Apache2::Const::DECLINED unless $template;
+
+ $template = $ctx->{skin} . "/$template";
+ $ctx->{page_args} = $page_args;
+ $r->content_type('text/html; encoding=utf8');
+
+ my $tt = Template->new({
+ OUTPUT => $r,
+ INCLUDE_PATH => $ctx->{template_paths},
+ });
+
+ unless($tt->process($template, {ctx => $ctx})) {
+ $r->log->warn('Template error: ' . $tt->error);
+ return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ return Apache2::Const::OK;
+}
+
+sub load_context {
+ my $r = shift;
+ my $cgi = CGI->new;
+ my $ctx = $web_config->{ctx};
+ $ctx->{skin} = $cgi->cookie(OILS_HTTP_COOKIE_SKIN) || 'default';
+ $ctx->{theme} = $cgi->cookie(OILS_HTTP_COOKIE_THEME) || 'default';
+ $ctx->{locale} =
+ $r->headers_in->get('Accept-Language') || # this will need some trimming
+ $cgi->cookie(OILS_HTTP_COOKIE_LOCALE) || 'en-US';
+ $r->log->debug('skin = ' . $ctx->{skin} . ' : theme = ' .
+ $ctx->{theme} . ' : locale = ' . $ctx->{locale});
+ return $ctx;
+}
+
+# Given a URI, finds the configured template and any extra page
+# arguments (trailing path info). Any extra data is returned
+# as page arguments, in the form of an array, one item per
+# /-separated URI component
+sub find_template {
+ my $r = shift;
+ my $base = shift;
+ my $path = $r->uri;
+ $path =~ s/$base//og;
+ my @parts = split('/', $path);
+ my $template = '';
+ my $page_args = [];
+ my $handler = $web_config->{handlers};
+ while(@parts) {
+ my $part = shift @parts;
+ next unless $part;
+ my $t = $handler->{$part};
+ if(ref $t) {
+ $handler = $t;
+ } else {
+ $template = $t;
+ $page_args = [@parts];
+ last;
+ }
+ }
+
+ unless($template) {
+ $r->log->warn("No template configured for path $path");
+ return ();
+ }
+
+ $r->log->debug("template = $template : page args = @$page_args");
+ return ($template, $page_args);
+}
+
+# if the web configuration file has never been loaded or has
+# changed since the last load, reload it
+sub check_web_config {
+ my $r = shift;
+ my $epoch = stat($web_config_file)->mtime;
+ unless($web_config_edit_time and $web_config_edit_time == $epoch) {
+ $r->log->debug("Reloading web config after edit...") if $r;
+ $web_config_edit_time = $epoch;
+ $web_config = parse_config($web_config_file);
+ }
+}
+
+sub parse_config {
+ my $cfg_file = shift;
+ my $data = XML::Simple->new->XMLin($cfg_file);
+ my $ctx = {};
+ my $handlers = {};
+
+ $ctx->{media_prefix} = (ref $data->{media_prefix}) ? '' : $data->{media_prefix};
+ $ctx->{base_uri} = (ref $data->{base_uri}) ? '' : $data->{base_uri};
+ $ctx->{template_paths} = [];
+
+ my $tpaths = $data->{template_paths}->{path};
+ $tpaths = [$tpaths] unless ref $tpaths;
+ push(@{$ctx->{template_paths}}, $_) for @$tpaths;
+
+ for my $handler (@{$data->{handlers}->{handler}}) {
+ my @parts = split('/', $handler->{path});
+ my $h = $handlers;
+ my $pcount = scalar(@parts);
+ for(my $i = 0; $i < $pcount; $i++) {
+ my $p = $parts[$i];
+ unless(defined $h->{$p}) {
+ if($i == $pcount - 1) {
+ $h->{$p} = $handler->{template};
+ last;
+ } else {
+ $h->{$p} = {};
+ }
+ }
+ $h = $h->{$p};
+ }
+ }
+
+ return {ctx => $ctx, handlers => $handlers};
+}
+
+
+1;
--- /dev/null
+DROP SCHEMA acq CASCADE;
+
+BEGIN;
+
+CREATE SCHEMA acq;
+
+
+-- Tables
+
+
+CREATE TABLE acq.currency_type (
+ code TEXT PRIMARY KEY,
+ label TEXT
+);
+
+-- Use the ISO 4217 abbreviations for currency codes
+INSERT INTO acq.currency_type (code, label) VALUES ('USD','US Dollars');
+INSERT INTO acq.currency_type (code, label) VALUES ('CAN','Canadian Dollars');
+INSERT INTO acq.currency_type (code, label) VALUES ('EUR','Euros');
+
+CREATE TABLE acq.exchange_rate (
+ id SERIAL PRIMARY KEY,
+ from_currency TEXT NOT NULL REFERENCES acq.currency_type (code),
+ to_currency TEXT NOT NULL REFERENCES acq.currency_type (code),
+ ratio NUMERIC NOT NULL,
+ CONSTRAINT exchange_rate_from_to_once UNIQUE (from_currency,to_currency)
+);
+
+INSERT INTO acq.exchange_rate (from_currency,to_currency,ratio) VALUES ('USD','CAN',1.2);
+INSERT INTO acq.exchange_rate (from_currency,to_currency,ratio) VALUES ('USD','EUR',0.5);
+
+CREATE TABLE acq.provider (
+ id SERIAL PRIMARY KEY,
+ name TEXT NOT NULL,
+ owner INT NOT NULL REFERENCES actor.org_unit (id),
+ currency_type TEXT NOT NULL REFERENCES acq.currency_type (code),
+ code TEXT UNIQUE,
+ CONSTRAINT provider_name_once_per_owner UNIQUE (name,owner)
+);
+
+CREATE TABLE acq.funding_source (
+ id SERIAL PRIMARY KEY,
+ name TEXT NOT NULL,
+ owner INT NOT NULL REFERENCES actor.org_unit (id),
+ currency_type TEXT NOT NULL REFERENCES acq.currency_type (code),
+ code TEXT UNIQUE,
+ CONSTRAINT funding_source_name_once_per_owner UNIQUE (name,owner)
+);
+
+CREATE TABLE acq.funding_source_credit (
+ id SERIAL PRIMARY KEY,
+ funding_source INT NOT NULL REFERENCES acq.funding_source (id),
+ amount NUMERIC NOT NULL,
+ note TEXT
+);
+
+CREATE TABLE acq.fund (
+ id SERIAL PRIMARY KEY,
+ org INT NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE,
+ name TEXT NOT NULL,
+ year INT NOT NULL DEFAULT EXTRACT( YEAR FROM NOW() ),
+ currency_type TEXT NOT NULL REFERENCES acq.currency_type (code),
+ code TEXT UNIQUE,
+ CONSTRAINT name_once_per_org_year UNIQUE (org,name,year)
+);
+
+CREATE TABLE acq.fund_debit (
+ id SERIAL PRIMARY KEY,
+ fund INT NOT NULL REFERENCES acq.fund (id),
+ origin_amount NUMERIC NOT NULL, -- pre-exchange-rate amount
+ origin_currency_type TEXT NOT NULL REFERENCES acq.currency_type (code),
+ amount NUMERIC NOT NULL,
+ encumbrance BOOL NOT NULL DEFAULT TRUE,
+ debit_type TEXT NOT NULL,
+ xfer_destination INT REFERENCES acq.fund (id)
+);
+
+CREATE TABLE acq.fund_allocation (
+ id SERIAL PRIMARY KEY,
+ funding_source INT NOT NULL REFERENCES acq.funding_source (id) ON UPDATE CASCADE ON DELETE CASCADE,
+ fund INT NOT NULL REFERENCES acq.fund (id) ON UPDATE CASCADE ON DELETE CASCADE,
+ amount NUMERIC,
+ percent NUMERIC CHECK (percent IS NULL OR percent BETWEEN 0.0 AND 100.0),
+ allocator INT NOT NULL REFERENCES actor.usr (id),
+ note TEXT,
+ CONSTRAINT allocation_amount_or_percent CHECK ((percent IS NULL AND amount IS NOT NULL) OR (percent IS NOT NULL AND amount IS NULL))
+);
+
+
+CREATE TABLE acq.picklist (
+ id SERIAL PRIMARY KEY,
+ owner INT NOT NULL REFERENCES actor.usr (id),
+ org_unit INT NOT NULL REFERENCES actor.org_unit (id),
+ name TEXT NOT NULL,
+ create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
+ edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
+ CONSTRAINT name_once_per_owner UNIQUE (name,owner)
+);
+
+CREATE TABLE acq.purchase_order (
+ id SERIAL PRIMARY KEY,
+ owner INT NOT NULL REFERENCES actor.usr (id),
+ ordering_agency INT NOT NULL REFERENCES actor.org_unit (id),
+ create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
+ edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
+ provider INT NOT NULL REFERENCES acq.provider (id),
+ state TEXT NOT NULL DEFAULT 'new'
+);
+CREATE INDEX po_owner_idx ON acq.purchase_order (owner);
+CREATE INDEX po_provider_idx ON acq.purchase_order (provider);
+CREATE INDEX po_state_idx ON acq.purchase_order (state);
+
+CREATE TABLE acq.po_note (
+ id SERIAL PRIMARY KEY,
+ purchase_order INT NOT NULL REFERENCES acq.purchase_order (id),
+ creator INT NOT NULL REFERENCES actor.usr (id),
+ editor INT NOT NULL REFERENCES actor.usr (id),
+ create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
+ edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
+ value TEXT NOT NULL
+);
+CREATE INDEX po_note_po_idx ON acq.po_note (purchase_order);
+
+CREATE TABLE acq.lineitem (
+ id BIGSERIAL PRIMARY KEY,
+ selector INT NOT NULL REFERENCES actor.org_unit (id),
+ provider INT REFERENCES acq.provider (id),
+ purchase_order INT REFERENCES acq.purchase_order (id),
+ picklist INT REFERENCES acq.picklist (id),
+ expected_recv_time TIMESTAMP WITH TIME ZONE,
+ create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
+ edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
+ marc TEXT NOT NULL,
+ eg_bib_id INT REFERENCES biblio.record_entry (id),
+ source_label TEXT,
+ item_count INT NOT NULL DEFAULT 0,
+ state TEXT NOT NULL DEFAULT 'new',
+ CONSTRAINT picklist_or_po CHECK (picklist IS NOT NULL OR purchase_order IS NOT NULL)
+);
+CREATE INDEX li_po_idx ON acq.lineitem (purchase_order);
+CREATE INDEX li_pl_idx ON acq.lineitem (picklist);
+
+CREATE TABLE acq.lineitem_note (
+ id SERIAL PRIMARY KEY,
+ lineitem INT NOT NULL REFERENCES acq.lineitem (id),
+ creator INT NOT NULL REFERENCES actor.usr (id),
+ editor INT NOT NULL REFERENCES actor.usr (id),
+ create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
+ edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
+ value TEXT NOT NULL
+);
+CREATE INDEX li_note_li_idx ON acq.lineitem_note (lineitem);
+
+CREATE TABLE acq.lineitem_detail (
+ id BIGSERIAL PRIMARY KEY,
+ lineitem INT NOT NULL REFERENCES acq.lineitem (id),
+ fund INT REFERENCES acq.fund (id),
+ fund_debit INT REFERENCES acq.fund_debit (id),
+ eg_copy_id BIGINT REFERENCES asset.copy (id) ON DELETE SET NULL,
+ barcode TEXT,
+ cn_label TEXT,
+ owning_lib INT REFERENCES actor.org_unit (id) ON DELETE SET NULL,
+ location INT REFERENCES asset.copy_location (id) ON DELETE SET NULL,
+ recv_time TIMESTAMP WITH TIME ZONE
+);
+
+CREATE INDEX li_detail_li_idx ON acq.lineitem_detail (lineitem);
+
+CREATE TABLE acq.lineitem_attr_definition (
+ id BIGSERIAL PRIMARY KEY,
+ code TEXT NOT NULL,
+ description TEXT NOT NULL,
+ remove TEXT NOT NULL DEFAULT '',
+ ident BOOL NOT NULL DEFAULT FALSE
+);
+
+CREATE TABLE acq.lineitem_marc_attr_definition (
+ id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
+ xpath TEXT NOT NULL
+) INHERITS (acq.lineitem_attr_definition);
+
+CREATE TABLE acq.lineitem_provider_attr_definition (
+ id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
+ xpath TEXT NOT NULL,
+ provider INT NOT NULL REFERENCES acq.provider (id)
+) INHERITS (acq.lineitem_attr_definition);
+
+CREATE TABLE acq.lineitem_generated_attr_definition (
+ id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
+ xpath TEXT NOT NULL
+) INHERITS (acq.lineitem_attr_definition);
+
+CREATE TABLE acq.lineitem_usr_attr_definition (
+ id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
+ usr INT NOT NULL REFERENCES actor.usr (id)
+) INHERITS (acq.lineitem_attr_definition);
+
+CREATE TABLE acq.lineitem_local_attr_definition (
+ id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq')
+) INHERITS (acq.lineitem_attr_definition);
+
+CREATE TABLE acq.lineitem_attr (
+ id BIGSERIAL PRIMARY KEY,
+ definition BIGINT NOT NULL,
+ lineitem BIGINT NOT NULL REFERENCES acq.lineitem (id),
+ attr_type TEXT NOT NULL,
+ attr_name TEXT NOT NULL,
+ attr_value TEXT NOT NULL
+);
+
+CREATE INDEX li_attr_li_idx ON acq.lineitem_attr (lineitem);
+CREATE INDEX li_attr_value_idx ON acq.lineitem_attr (attr_value);
+CREATE INDEX li_attr_definition_idx ON acq.lineitem_attr (definition);
+
+
+-- Seed data
+
+
+INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('title','Title of work','//*[@tag="245"]/*[contains("abcmnopr",@code)]');
+INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('author','Author of work','//*[@tag="100" or @tag="110" or @tag="113"]/*[contains("ad",@code)]');
+INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('language','Lanuage of work','//*[@tag="240"]/*[@code="l"][1]');
+INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('pagination','Pagination','//*[@tag="300"]/*[@code="a"][1]');
+INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath, remove ) VALUES ('isbn','ISBN','//*[@tag="020"]/*[@code="a"]', $r$(?:-|\s.+$)$r$);
+INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath, remove ) VALUES ('issn','ISSN','//*[@tag="022"]/*[@code="a"]', $r$(?:-|\s.+$)$r$);
+INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('price','Price','//*[@tag="020" or @tag="022"]/*[@code="c"][1]');
+INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('identifier','Identifier','//*[@tag="001"]');
+INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('publisher','Publisher','//*[@tag="260"]/*[@code="b"][1]');
+INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('pubdate','Publication Date','//*[@tag="260"]/*[@code="c"][1]');
+INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('edition','Edition','//*[@tag="250"]/*[@code="a"][1]');
+
+
+-- Functions
+
+
+CREATE OR REPLACE FUNCTION public.extract_acq_marc_field ( BIGINT, TEXT, TEXT) RETURNS TEXT AS $$
+ SELECT public.extract_marc_field('acq.lineitem', $1, $2, $3);
+$$ LANGUAGE SQL;
+
+/*
+CREATE OR REPLACE FUNCTION public.extract_bib_marc_field ( BIGINT, TEXT ) RETURNS TEXT AS $$
+ SELECT public.extract_marc_field('biblio.record_entry', $1, $2);
+$$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION public.extract_authority_marc_field ( BIGINT, TEXT ) RETURNS TEXT AS $$
+ SELECT public.extract_marc_field('authority.record_entry', $1, $2);
+$$ LANGUAGE SQL;
+*/
+-- For example:
+-- INSERT INTO acq.lineitem_provider_attr_definition ( provider, code, description, xpath ) VALUES (1,'price','Price','//*[@tag="020" or @tag="022"]/*[@code="a"][1]');
+
+/*
+Suggested vendor fields:
+ vendor_price
+ vendor_currency
+ vendor_avail
+ vendor_po
+ vendor_identifier
+*/
+
+CREATE OR REPLACE FUNCTION public.ingest_acq_marc ( ) RETURNS TRIGGER AS $$
+DECLARE
+ value TEXT;
+ atype TEXT;
+ prov INT;
+ adef RECORD;
+ xpath_string TEXT;
+BEGIN
+ FOR adef IN SELECT *,tableoid FROM acq.lineitem_attr_definition LOOP
+
+ SELECT relname::TEXT INTO atype FROM pg_class WHERE oid = adef.tableoid;
+
+ IF (atype NOT IN ('lineitem_usr_attr_definition','lineitem_local_attr_definition')) THEN
+ IF (atype = 'lineitem_provider_attr_definition') THEN
+ SELECT provider INTO prov FROM acq.lineitem_provider_attr_definition WHERE id = adef.id;
+ CONTINUE WHEN NEW.provider IS NULL OR prov <> NEW.provider;
+ END IF;
+
+ IF (atype = 'lineitem_provider_attr_definition') THEN
+ SELECT xpath INTO xpath_string FROM acq.lineitem_provider_attr_definition WHERE id = adef.id;
+ ELSIF (atype = 'lineitem_marc_attr_definition') THEN
+ SELECT xpath INTO xpath_string FROM acq.lineitem_marc_attr_definition WHERE id = adef.id;
+ ELSIF (atype = 'lineitem_generated_attr_definition') THEN
+ SELECT xpath INTO xpath_string FROM acq.lineitem_generated_attr_definition WHERE id = adef.id;
+ END IF;
+
+ SELECT extract_acq_marc_field(id, xpath_string, adef.remove) INTO value FROM acq.lineitem WHERE id = NEW.id;
+
+ IF (value IS NOT NULL AND value <> '') THEN
+ INSERT INTO acq.lineitem_attr (lineitem, definition, attr_type, attr_name, attr_value)
+ VALUES (NEW.id, adef.id, atype, adef.code, value);
+ END IF;
+
+ END IF;
+
+ END LOOP;
+
+ RETURN NULL;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION public.cleanup_acq_marc ( ) RETURNS TRIGGER AS $$
+BEGIN
+ IF TG_OP = 'UPDATE' THEN
+ DELETE FROM acq.lineitem_attr
+ WHERE lineitem = OLD.id AND attr_type IN ('lineitem_provider_attr_definition', 'lineitem_marc_attr_definition','lineitem_generated_attr_definition');
+ RETURN NEW;
+ ELSE
+ DELETE FROM acq.lineitem_attr WHERE lineitem = OLD.id;
+ RETURN OLD;
+ END IF;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE TRIGGER cleanup_lineitem_trigger
+ BEFORE UPDATE OR DELETE ON acq.lineitem
+ FOR EACH ROW EXECUTE PROCEDURE public.cleanup_acq_marc();
+
+CREATE TRIGGER ingest_lineitem_trigger
+ AFTER INSERT OR UPDATE ON acq.lineitem
+ FOR EACH ROW EXECUTE PROCEDURE public.ingest_acq_marc();
+
+CREATE OR REPLACE FUNCTION acq.exchange_ratio ( from_ex TEXT, to_ex TEXT ) RETURNS NUMERIC AS $$
+DECLARE
+ rat NUMERIC;
+BEGIN
+ IF from_ex = to_ex THEN
+ RETURN 1.0;
+ END IF;
+
+ SELECT ratio INTO rat FROM acq.exchange_rate WHERE from_currency = from_ex AND to_currency = to_ex;
+
+ IF FOUND THEN
+ RETURN rat;
+ ELSE
+ SELECT ratio INTO rat FROM acq.exchange_rate WHERE from_currency = to_ex AND to_currency = from_ex;
+ IF FOUND THEN
+ RETURN 1.0/rat;
+ END IF;
+ END IF;
+
+ RETURN NULL;
+
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION acq.exchange_ratio ( TEXT, TEXT, NUMERIC ) RETURNS NUMERIC AS $$
+ SELECT $3 * acq.exchange_ratio($1, $2);
+$$ LANGUAGE SQL;
+
+CREATE OR REPLACE VIEW acq.funding_source_credit_total AS
+ SELECT funding_source,
+ SUM(amount) AS amount
+ FROM acq.funding_source_credit
+ GROUP BY 1;
+
+CREATE OR REPLACE VIEW acq.funding_source_allocation_total AS
+ SELECT funding_source,
+ SUM(amount)::NUMERIC(100,2) AS amount
+ FROM (
+ SELECT funding_source,
+ SUM(a.amount)::NUMERIC(100,2) AS amount
+ FROM acq.fund_allocation a
+ WHERE a.percent IS NULL
+ GROUP BY 1
+ UNION ALL
+ SELECT funding_source,
+ SUM( (SELECT SUM(amount) FROM acq.funding_source_credit c WHERE c.funding_source = a.funding_source) * (a.percent/100.0) )::NUMERIC(100,2) AS amount
+ FROM acq.fund_allocation a
+ WHERE a.amount IS NULL
+ GROUP BY 1
+ ) x
+ GROUP BY 1;
+
+CREATE OR REPLACE VIEW acq.funding_source_balance AS
+ SELECT COALESCE(c.funding_source, a.funding_source) AS funding_source,
+ SUM(COALESCE(c.amount,0.0) - COALESCE(a.amount,0.0))::NUMERIC(100,2) AS amount
+ FROM acq.funding_source_credit_total c
+ FULL JOIN acq.funding_source_allocation_total a USING (funding_source)
+ GROUP BY 1;
+
+CREATE OR REPLACE VIEW acq.fund_allocation_total AS
+ SELECT fund,
+ SUM(amount)::NUMERIC(100,2) AS amount
+ FROM (
+ SELECT fund,
+ SUM(a.amount * acq.exchange_ratio(s.currency_type, f.currency_type))::NUMERIC(100,2) AS amount
+ FROM acq.fund_allocation a
+ JOIN acq.fund f ON (a.fund = f.id)
+ JOIN acq.funding_source s ON (a.funding_source = s.id)
+ WHERE a.percent IS NULL
+ GROUP BY 1
+ UNION ALL
+ SELECT fund,
+ SUM( (SELECT SUM(amount) FROM acq.funding_source_credit c WHERE c.funding_source = a.funding_source) * acq.exchange_ratio(s.currency_type, f.currency_type) * (a.percent/100.0) )::NUMERIC(100,2) AS amount
+ FROM acq.fund_allocation a
+ JOIN acq.fund f ON (a.fund = f.id)
+ JOIN acq.funding_source s ON (a.funding_source = s.id)
+ WHERE a.amount IS NULL
+ GROUP BY 1
+ ) x
+ GROUP BY 1;
+
+CREATE OR REPLACE VIEW acq.fund_debit_total AS
+ SELECT id AS fund,
+ encumbrance,
+ SUM(amount) AS amount
+ FROM acq.fund_debit
+ GROUP BY 1,2;
+
+CREATE OR REPLACE VIEW acq.fund_encumbrance_total AS
+ SELECT fund,
+ SUM(amount) AS amount
+ FROM acq.fund_debit_total
+ WHERE encumbrance IS TRUE
+ GROUP BY 1;
+
+CREATE OR REPLACE VIEW acq.fund_spent_total AS
+ SELECT fund,
+ SUM(amount) AS amount
+ FROM acq.fund_debit_total
+ WHERE encumbrance IS FALSE
+ GROUP BY 1;
+
+CREATE OR REPLACE VIEW acq.fund_combined_balance AS
+ SELECT c.fund,
+ c.amount - COALESCE(d.amount,0.0) AS amount
+ FROM acq.fund_allocation_total c
+ LEFT JOIN acq.fund_debit_total d USING (fund);
+
+CREATE OR REPLACE VIEW acq.fund_spent_balance AS
+ SELECT c.fund,
+ c.amount - COALESCE(d.amount,0.0) AS amount
+ FROM acq.fund_allocation_total c
+ LEFT JOIN acq.fund_spent_total d USING (fund);
+
+COMMIT;
+
+
+
+
--- /dev/null
+
+
+DROP SCHEMA serial CASCADE;
+
+CREATE TABLE asset.uri (
+ id SERIAL PRIMARY KEY,
+ href TEXT NOT NULL,
+ label TEXT,
+ use TEXT,
+ active BOOL NOT NULL DEFAULT TRUE
+);
+
+ALTER TABLE asset.call_number ADD COLUMN uri INT REFERENCES asset.uri (id);
+
+BEGIN;
+
+CREATE SCHEMA serial;
+
+CREATE TABLE serial.subscription (
+ id SERIAL PRIMARY KEY,
+ callnumber BIGINT REFERENCES asset.call_number (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
+ uri INT REFERENCES asset.uri (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
+ start_date DATE NOT NULL,
+ end_date DATE NOT NULL
+);
+
+CREATE TABLE serial.binding_unit (
+ id SERIAL PRIMARY KEY,
+ subscription INT NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+ label TEXT NOT NULL,
+ CONSTRAINT bu_label_once_per_sub UNIQUE (subscription, label)
+);
+
+CREATE TABLE serial.issuance (
+ id SERIAL PRIMARY KEY,
+ subscription INT NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+ target_copy BIGINT REFERENCES asset.copy (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+ binding_unit INT REFERENCES serial.binding_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+ label TEXT
+);
+
+CREATE TABLE serial.bib_summary (
+ id SERIAL PRIMARY KEY,
+ call_number INT UNIQUE NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+ generated_coverage TEXT NOT NULL,
+ textual_holdings TEXT
+);
+
+CREATE TABLE serial.sup_summary (
+ id SERIAL PRIMARY KEY,
+ call_number INT UNIQUE NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+ generated_coverage TEXT NOT NULL,
+ textual_holdings TEXT
+);
+
+CREATE TABLE serial.index_summary (
+ id SERIAL PRIMARY KEY,
+ call_number INT UNIQUE NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+ generated_coverage TEXT NOT NULL,
+ textual_holdings TEXT
+);
+
+COMMIT;
+
INSERT INTO permission.perm_list VALUES
(151, 'DELETE_CONTAINER_ITEM', oils_i18n_gettext(151, 'Allow a user to delete an item out of another user''s container', 'ppl', 'description'));
INSERT INTO permission.perm_list VALUES
+ (153, 'CREATE_FUNDING_SOURCE', oils_i18n_gettext(153, 'Allow a user to create a new funding source', 'ppl', 'description')),
+ (154, 'DELETE_FUNDING_SOURCE', oils_i18n_gettext(154, 'Allow a user to delete a funding source', 'ppl', 'description')),
+ (155, 'VIEW_FUNDING_SOURCE', oils_i18n_gettext(155, 'Allow a user to view a funding source', 'ppl', 'description')),
+ (156, 'UPDATE_FUNDING_SOURCE', oils_i18n_gettext(156, 'Allow a user to update a funding source', 'ppl', 'description')),
+ (157, 'CREATE_FUND', oils_i18n_gettext(157, 'Allow a user to create a new fund', 'ppl', 'description')),
+ (158, 'DELETE_FUND', oils_i18n_gettext(158, 'Allow a user to delete a fund', 'ppl', 'description')),
+ (159, 'VIEW_FUND', oils_i18n_gettext(159, 'Allow a user to view a fund', 'ppl', 'description')),
+ (160, 'UPDATE_FUND', oils_i18n_gettext(160, 'Allow a user to update a fund', 'ppl', 'description')),
+ (161, 'CREATE_FUND_ALLOCATION', oils_i18n_gettext(161, 'Allow a user to create a new fund allocation', 'ppl', 'description')),
+ (162, 'DELETE_FUND_ALLOCATION', oils_i18n_gettext(162, 'Allow a user to delete a fund allocation', 'ppl', 'description')),
+ (163, 'VIEW_FUND_ALLOCATION', oils_i18n_gettext(163, 'Allow a user to view a fund allocation', 'ppl', 'description')),
+ (164, 'UPDATE_FUND_ALLOCATION', oils_i18n_gettext(164, 'Allow a user to update a fund allocation', 'ppl', 'description')),
+ (165, 'GENERAL_ACQ', oils_i18n_gettext(165, 'Lowest level permission required to access the ACQ interface', 'ppl', 'description')),
+ (166, 'CREATE_PROVIDER', oils_i18n_gettext(166, 'Allow a user to create a new provider', 'ppl', 'description')),
+ (167, 'DELETE_PROVIDER', oils_i18n_gettext(167, 'Allow a user to delate a provider', 'ppl', 'description')),
+ (168, 'VIEW_PROVIDER', oils_i18n_gettext(168, 'Allow a user to view a provider', 'ppl', 'description')),
+ (169, 'UPDATE_PROVIDER', oils_i18n_gettext(169, 'Allow a user to update a provider', 'ppl', 'description')),
+ (170, 'ADMIN_FUNDING_SOURCE', oils_i18n_gettext(170, 'Allow a user to create/view/update/delete a funding source', 'ppl', 'description')),
+ (171, 'ADMIN_FUND', oils_i18n_gettext(171, 'Allow a user to create/view/update/delete a fund', 'ppl', 'description')),
+ (172, 'MANAGE_FUNDING_SOURCE', oils_i18n_gettext(172, 'Allow a user to view/credit/debit a funding source', 'ppl', 'description')),
+ (173, 'MANAGE_FUND', oils_i18n_gettext(173, 'Allow a user to view/credit/debit a fund', 'ppl', 'description')),
+ (174, 'CREATE_PICKLIST', oils_i18n_gettext(174, 'Allows a user to create a picklist', 'ppl', 'description')),
+ (175, 'ADMIN_PROVIDER', oils_i18n_gettext(175, 'Allow a user to create/view/update/delete a provider', 'ppl', 'description')),
+ (176, 'MANAGE_PROVIDER', oils_i18n_gettext(176, 'Allow a user to view and purchase from a provider', 'ppl', 'description')),
+ (177, 'VIEW_PICKLIST', oils_i18n_gettext(177, 'Allow a user to view another users picklist', 'ppl', 'description')),
(152, 'ASSIGN_WORK_ORG_UNIT', oils_i18n_gettext(152, 'Allow a staff member to define where another staff member has their permissions', 'ppl', 'description'));
INSERT INTO permission.perm_list VALUES
- (153, 'DELETE_RECORD', oils_i18n_gettext(153, 'Allow a staff member to directly remove a bibliographic record', 'ppl', 'description'));
+ (178, 'DELETE_RECORD', oils_i18n_gettext(178, 'Allow a staff member to directly remove a bibliographic record', 'ppl', 'description'));
+INSERT INTO permission.perm_list VALUES
+ (179, 'ADMIN_CURRENCY_TYPE', oils_i18n_gettext(179, 'Allow a user to create/view/update/delete a currency_type', 'ppl', 'description'));
SELECT SETVAL('permission.perm_list_id_seq'::TEXT, (SELECT MAX(id) FROM permission.perm_list));
100.circ_matrix.sql
110.hold_matrix.sql
+ 200.schema.acq.sql
+ 210.schema.serials.sql
+
300.schema.staged_search.sql
500.view.cross-schema.sql
--- /dev/null
+#!/usr/bin/python
+import sys
+import oils.system, oils.utils.utils
+import osrf.net_obj, osrf.ses
+
+# ---------------------------------------------------------------
+# Usage: python acq_fund_source.py <user> <password> <workstation>
+# ---------------------------------------------------------------
+
+oils.system.System.connect(config_file='/openils/conf/opensrf_core.xml', config_context='config.opensrf')
+auth_info = oils.utils.utils.login(sys.argv[1], sys.argv[2], 'staff', sys.argv[3])
+authtoken = auth_info['payload']['authtoken']
+
+ses = osrf.ses.ClientSession('open-ils.acq')
+ses.connect() # not required, but faster for batches of request
+
+# XXX This loop assumes the existence of orgs with IDs 1-6 and a USD currency
+ids = []
+for i in range(0,5):
+ fund_source = osrf.net_obj.NetworkObject.acqfs()
+ fund_source.name("test-fund_source-%d" % i)
+ fund_source.owner(i+1)
+ fund_source.currency_type('USD')
+ req = ses.request('open-ils.acq.funding_source.create', authtoken, fund_source)
+ id = req.recv().content()
+ print 'created fund_source ' + str(id)
+ ids.append(id)
+
+req = ses.request('open-ils.acq.funding_source.org.retrieve', authtoken, 1, {"children":1})
+resp = req.recv().content()
+for fund_source in resp:
+ print 'fetched fund_source ' + str(fund_source.name())
+
+for i in ids:
+ req = ses.request('open-ils.acq.funding_source.delete', authtoken, i)
+ print 'delete returned ' + str(req.recv().content())
+
+
+ses.disconnect() # only required if a connect() call was made
+
+
--- /dev/null
+/* import the default css for the install applications */
+@import "default/acq.css";
+@import "default/admin.css";
+/* import the dojo CSS */
+@import "/js/dojo/dojo/resources/dojo.css";
+@import "/js/dojo/dijit/themes/tundra/tundra.css";
+@import "/js/dojo/dojox/grid/_grid/Grid.css";
+
+
+html, body, #oils-base-body-block {
+ width:100%;
+ height:100%;
+ border:0;
+ margin:0;
+ padding:0;
+}
+table { border-collapse: collapse; }
+/* use this for divs whose contents should be entirely contained within the div */
+.container:after {content: ""; display: block; height: 0; clear: both; }
+
+.invisible { visibility: hidden; }
+.hidden { display: none; visibility: hidden; }
+.display { display: block; visibility: visible; }
+.oils-login-dialog td { padding: 5px; }
+
+/* main layout blocks */
+#oils-base-main-block { width: 100%; margin-top: 0px; padding-top: 0px;}
+#oils-base-navigate-block { width: 12%; vertical-align: top; float:left;}
+#oils-base-content-block { width: 87%; vertical-align: top; float:right; padding-top: 0px;}
+#oils-base-sidebar-block { width: 12%; vertical-align: top; float:left;}
+
+#oils-base-header-auto-login { padding-right: 20px; }
+#oils-base-header-block { width: 100%; text-align: right; margin-top: 0px; padding-bottom: 0px;}
+#oils-base-header-menu-block { float:left; text-align: left; width: 50%; }
+#oils-base-header-auto-login-block { float:right; text-align: right; width: 47%;}
+
+#oils-base-footer-block { width: 100%; text-align: center; vertical-align: bottom;}
+
+#oils-base-navigate-list { width: 100%; }
+.oils-base-navigate-sub-list { padding-left: 4px; }
+.oils-base-navigate-item {}
+
+/* general purpose form table */
+.oils-admin-table { width: 100%; }
+.oils-admin-table td { padding: 4px; }
+.oils-admin-table textarea { width: 400px; height: 40px; overflow:auto;}
+.oils-admin-label { width: auto; }
+
+.label { margin: 1px; }
+
+
+/* local dojo style enhancements ----------------------------------- */
+/*
+.dojoxGrid {border: 1px solid #333; height: 90%;}
+*/
+/*
+.dojoxGrid {height: 90%;}
+.dojoxGrid-cell {padding: 8px;}
+*/
+.dijitTooltipTable td {padding: 3px;} /* custom class for handling dialog tables */
+/* ----------------------------------------------------------------- */
+
--- /dev/null
+.spacer {clear: both}
+
+#oils-acq-index-block { font-weight:bold; }
+/*
+.oils-sub-navigate-block { width: 100%; text-align: left; padding: 3px;}
+.oils-sub-navigate-block span { padding: 3px; }
+*/
+
+.oils-acq-detail-content-pane {height:600px;width:100%}
+.oils-acq-basic-form-table td {padding:4px;}
+
+/* bib search */
+#oils-acq-search-container { width:100%; }
+#oils-acq-search-sources-block { width:32%; vertical-align: top; float: left; margin-right: 10px;}
+#oils-acq-search-form-block { width:63%; vertical-align: top; float:right; }
+#oils-acq-search-sources-selector { padding: 2px; }
+#oils-acq-search-sources-selector option { margin-bottom: 2px; }
+.oils-acq-search-form-row { width: 100%; }
+.oils-acq-search-form-label {}
+.oils-acq-search-form-input {}
+#oils-acq-search-sources-list { padding: 1px; }
+#oils-acq-search-sources-list li { list-style-type: none; padding-left: 0px; }
+.oils-acq-search-sources-sublist { padding: 1px; list-style-type: none;}
+.oils-acq-search-sources-sublist li { margin-left: 10px; }
+.oils-acq-search-subsources-label { margin-top: 5px; }
+#oils-acq-search-sources-label { margin-bottom: 10px; }
+#oils-acq-search-fields-label { margin-bottom: 10px; }
+#oils-acq-search-fields-submit-block { margin: 5px; text-align: center;}
+#oils-acq-search-progress {width: 100%; text-align: center;}
+#oils-acq-search-source-select option {padding: 5px;}
+#oils-acq-search-fields-tbody td {padding: 3px;}
+
+/* list of picklists */
+#oils-acq-picklist-list-table {width: 100%;}
+#oils-acq-picklist-list-table td {padding: 3px;}
+
+/* a single picklist */
+#oils-acq-picklist-table { width: 100%; }
+#oils-acq-picklist-header { padding: 4px; margin-bottom: 20px; }
+.oils-acq-picklist-records-jacket-td { width: 46px; height: 54px; }
+.oils-acq-picklist-records-jacket { width: 42px; height: 54px; padding-left: 0px; }
+.oils-acq-picklist-records-title {vertical-align: top}
+.oils-acq-picklist-records-copies {text-align: right; width: 200px}
+#oils-acq-picklist-paging-block { width: 25%; float: left; position: relative; }
+#oils-acq-picklist-actions-block { width: 75%; text-align: right; float: right; position: relative; }
+#oils-acq-picklist-header-subtable { width: 100%; }
+#oils-acq-picklist-header-block { width: 100%; display: block }
+
+/* list of pos */
+#oils-acq-po-list-table {width: 100%;}
+#oils-acq-po-list-table td {padding: 3px;}
+
+/* a single po */
+#oils-acq-po-table { width: 100%; }
+#oils-acq-po-header { padding: 4px; margin-bottom: 20px; }
+.oils-acq-po-records-jacket-td { width: 46px; }
+.oils-acq-po-records-jacket { width: 42px; height: 54px; padding-left: 0px; }
+.oils-acq-po-records-title-row {}
+.oils-acq-po-records-author-row td { padding-left: 30px; }
+.oils-acq-po-records-phys_desc-row td { padding-left: 30px; }
+.oils-acq-po-records-phys_desc-row {}
+
+#oils-acq-po-paging-block { width: 50%; text-align: left;}
+#oils-acq-po-actions-block { width: 50%; text-align: right;}
+#oils-acq-po-header-subtable { width: 100%; }
+
+#oils-acq-list-header { margin: 10px; width: 98%;}
+#oils-acq-list-header-label { float: left; }
+#oils-acq-list-header-actions { float: right; }
+
+/* purchase order line item detail page */
+#oils-acq-po-li-header { padding: 4px; margin-bottom: 20px; }
+#oils-acq-po-li-summary {}
+#oils-acq-po-li-summary td {padding: 2px;}
+.oils-acq-po-li-attr {}
+.oils-acq-po-li-attr-type {}
+.oils-acq-po-li-attr-name {}
+.oils-acq-po-li-attr-value {}
+#oils-acq-po-li-marc-block { margin-top: 10px; padding: 6px; }
+#oils-acq-po-li-details-table { width: 100%; }
+.oils-acq-po-li-detail-row {}
+
+/* picklist entry page */
+#oils-acq-lineitem-header { padding: 4px; margin-bottom: 20px; }
+#oils-acq-lineitem-summary {}
+#oils-acq-lineitem-summary td {padding: 2px;}
+.oils-acq-lineitem-attr {}
+.oils-acq-lineitem-attr-type {}
+.oils-acq-lineitem-attr-name {}
+.oils-acq-lineitem-attr-value {}
+#oils-acq-lineitem-marc-block { margin-top: 10px; padding: 6px; }
+
+
+
--- /dev/null
+#oils-admin-object-actions { width: 100%; padding: 2px; margin: 2px; text-align: right;}
+#oils-admin-object-table { width: 100%; }
+#oils-admin-object-table td { padding: 3px; }
--- /dev/null
+/* import the default css for the install applications */
+@import "default/acq.css";
+@import "default/admin.css";
+
+body { font-size: 80%; }
+
+#oils-base-body-block {}
+/*
+#oils-base-navigate-block {border: 2px solid #85C777; background: #6BA160;}
+#oils-base-navigate-block {border: 2px solid #f8f7f1; background: #fffdf1;}
+*/
+#oils-base-navigate-block {background: #d9e8f9;}
+
+#oils-base-navigate-block a { color: #000000; }
+#oils-base-content-block {}
+#oils-base-sidebar-block {}
+#oils-base-footer-block {padding: 3px; margin-top: 20px; border-top: 1px solid #5E5E5E;}
+/*
+#oils-base-header-block {border-bottom: 1px solid #5E5E5E; border: 2px solid #85C777; background: #6BA160;}
+*/
+#oils-base-header-block {border-bottom: 1px solid #d9e8f9;}
+
+
+#oils-base-navigate-list { width: 100%; }
+.oils-base-navigate-sub-list { margin-left: 14px; }
+/*.oils-base-navigate-item {border: 2px solid #85C777; background: #6BA160;}*/
+.oils-base-navigate-item:hover { background: #85C777; }
+
+/*
+#oils-base-navigate-table td:hover { background: #85C777; }
+.oils-base-sub-navigate-block { border: 2px solid #6BA160; background: #85C777;}
+.oils-base-sub-navigate-block a { color: #000000; }
+*/
+
+#oils-base-header-user-info { font-weight: bold; margin-right: 8px;}
+
+
+.label { font-weight: bold; }
+
+/*
+.oils-admin-table td { border-bottom: 1px solid #5E5E5E; }
+*/
+.oils-admin-table td { border-bottom: 1px solid #d9e8f9; }
+.oils-admin-label { font-weight: bold; }
+
+.oils-acq-nav-link {
+ padding:0px 3px 3px 3px;
+ border-top:1px solid #F8F7F1;
+ border-bottom:1px solid #F8F7F1;
+ font-size: 105%;
+}
+.oils-acq-nav-link a { text-decoration:none; }
+.oils-acq-nav-link-active {
+ background:#FFFDF3;
+ border-top:1px solid #ACA899;
+ border-bottom:1px solid #ACA899;
+}
+
--- /dev/null
+
+#oils-acq-index-div { font-weight:bold; }
+
+#oils-acq-search-container { width:100%; }
+#oils-acq-search-sources-div { width:20%; float:left; }
+#oils-acq-search-form-div { width:80%; float:right; }
+#oils-acq-search-z39-sources-table thead td { font-weight: bold; }
+#oils-acq-search-z39-sources-table tbody td { width: 33%; }
+#oils-acq-search-sources-label { font-weight: bold; border-bottom: 1px solid #6BA160;}
+#oils-acq-search-fields-label { font-weight: bold; border-bottom: 1px solid #6BA160;}
+#oils-acq-search-subsources-label { font-weight: bold; }
+#oils-acq-search-fields-submit-block { border: 2px solid #A1A1A1; }
+
+/* list of picklists */
+#oils-acq-picklist-list-table {width: 100%;}
+#oils-acq-picklist-list-table thead td {font-weight:bold;}
+
+/* picklist display */
+#oils-acq-picklist-table thead tr { border: 1px solid #A1A1A1; }
+#oils-acq-picklist-header {border: 1px solid #85C777;}
+#oils-acq-lineitem-header {border: 1px solid #85C777;}
+#oils-acq-picklist-name { font-weight: bold; font-style: italic; }
+.oils-acq-picklist-attributes { font-size: 90%; margin-left: 15px;}
+.oils-acq-lineitem-attributes { font-size: 90%; margin-left: 15px;}
+.oils-acq-picklist-records-phys_desc-row { border-bottom: 1px solid #6BA160; }
+.oils-acq-picklist-picklist-td { border-style: solid; border-color: #A1A1A1; border-width: 0px 1px 0px 1px; }
+.oils-acq-picklist-records-service-td { font-size: 85%; }
+.oils-acq-lineitem-delete-link { font-size: 85%; }
+#oils-acq-picklist-header-subtable tr { border: none; }
+
+/* po display */
+#oils-acq-po-table thead tr { border: 1px solid #A1A1A1; }
+#oils-acq-po-header {border: 1px solid #85C777;}
+#oils-acq-po-li-header {border: 1px solid #85C777;}
+#oils-acq-po-name { font-weight: bold; font-style: italic; }
+.oils-acq-po-attributes { font-size: 90%; margin-left: 15px;}
+.oils-acq-po-li-attributes { font-size: 90%; margin-left: 15px;}
+.oils-acq-po-records-phys_desc-row { border-bottom: 1px solid #6BA160; }
+.oils-acq-po-po-td { border-style: solid; border-color: #A1A1A1; border-width: 0px 1px 0px 1px; }
+.oils-acq-po-records-service-td { font-size: 85%; }
+.oils-acq-po-li-delete-link { font-size: 85%; }
+#oils-acq-po-header-subtable tr { border: none; }
+
+/*
+#oils-acq-list-header {border-bottom: 1px solid #6BA160;}
+*/
+#oils-acq-list-header {border-bottom: 2px solid #d9e8f9;}
+#oils-acq-list-header-label { font-weight: bold; font-size: 110%; }
+#oils-acq-list-header-actions { font-weight: bold; }
+
+
+/* entry display */
+#oils-acq-lineitem-marc-block { border: 1px solid #6BA160; }
--- /dev/null
+#oils-admin-object-table tr { border-bottom: 1px solid #6BA160; }
--- /dev/null
+/* ---------------------------------------------------------------------------
+ * Copyright (C) 2008 Georgia Public Library Service
+ * Bill Erickson <erickson@esilibrary.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * ---------------------------------------------------------------------------
+ */
+
+
+/**
+ * General purpose, static utility functions
+ */
+
+if(!dojo._hasResource["openils.Util"]) {
+ dojo._hasResource["openils.Util"] = true;
+ dojo.provide("openils.Util");
+ dojo.require('openils.Event');
+ dojo.declare('openils.Util', null, {});
+
+
+ /**
+ * Wrapper for dojo.addOnLoad that verifies a valid login session is active
+ * before adding the function to the onload set
+ */
+ openils.Util.addOnLoad = function(func, noSes) {
+ if(func) {
+ if(!noSes) {
+ dojo.require('openils.User');
+ if(!openils.User.authtoken)
+ return;
+ }
+ console.log("adding onload " + func.name);
+ dojo.addOnLoad(func);
+ }
+ };
+
+ /**
+ * Returns true if the provided array contains the specified value
+ */
+ openils.Util.arrayContains = function(arr, val) {
+ for(var i = 0; arr && i < arr.length; i++) {
+ if(arr[i] == val)
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * Parses opensrf response objects to see if they contain
+ * data and/or an ILS event. This only calls request.recv()
+ * once, so in a streaming context, it's necessary to loop on
+ * this method.
+ * @param r The OpenSRF Request object
+ * @param eventOK If true, any found events will be returned as responses.
+ * If false, they will be treated as error conditions and their content will
+ * be alerted if openils.Util.alertEvent is set to true. Also, if eventOk is
+ * false, the response content will be null when an event is encountered.
+ */
+ openils.Util.alertEvent = true;
+ openils.Util.extractResponse = function(r, eventOk) {
+ var msg = r.recv();
+ if(msg == null) return msg;
+ var val = msg.content();
+ if(e = openils.Event.parse(val)) {
+ if(eventOk) return e;
+ console.log(e.toString());
+ if(openils.Util.alertEvent)
+ alert(e);
+ return null;
+ }
+ return val;
+ };
+
+}
--- /dev/null
+/* ---------------------------------------------------------------------------
+ * Copyright (C) 2008 Georgia Public Library Service
+ * Bill Erickson <erickson@esilibrary.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * ---------------------------------------------------------------------------
+ */
+
+if(!dojo._hasResource["openils.acq.CurrencyType"]) {
+
+ dojo._hasResource["openils.acq.CurrencyType"] = true;
+ dojo.provide("openils.acq.CurrencyType");
+ dojo.require('openils.User');
+
+ dojo.declare('openils.acq.CurrencyType', null, {
+ });
+
+ openils.acq.CurrencyType.cache = {};
+
+ /**
+ * Retrieves all of the currency types
+ */
+ openils.acq.CurrencyType.fetchAll = function(onComplete) {
+ var req = new OpenSRF.ClientSession('open-ils.acq').request(
+ 'open-ils.acq.currency_type.all.retrieve', openils.User.authtoken);
+
+ req.oncomplete = function(r) {
+ var msg = r.recv();
+ var types = msg.content();
+ for(var i in types)
+ openils.acq.CurrencyType.cache[types[i].code()] = types[i];
+ onComplete(types);
+ }
+ req.send();
+ }
+
+ openils.acq.CurrencyType.loadSelectWidget = function(selector) {
+ openils.acq.CurrencyType.fetchAll(
+ function(ctypes) {
+ selector.store = new dojo.data.ItemFileReadStore(
+ {data:acqct.toStoreData(ctypes, 'code', {identifier:'code'})});
+ selector.setValue(ctypes[0].code()); /* XXX get from setting */
+ }
+ );
+ }
+}
+
--- /dev/null
+/* ---------------------------------------------------------------------------
+ * Copyright (C) 2008 Georgia Public Library Service
+ * Bill Erickson <erickson@esilibrary.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * ---------------------------------------------------------------------------
+ */
+
+if(!dojo._hasResource['openils.acq.Fund']) {
+dojo._hasResource['openils.acq.Fund'] = true;
+dojo.provide('openils.acq.Fund');
+dojo.require('fieldmapper.Fieldmapper');
+dojo.require('fieldmapper.dojoData');
+dojo.require('openils.Event');
+
+/** Declare the Fund class with dojo */
+dojo.declare('openils.acq.Fund', null, {
+ /* add instance methods here if necessary */
+});
+
+openils.acq.Fund.cache = {};
+openils.acq.Fund._cachecomplete = false;
+
+openils.acq.Fund.createStore = function(onComplete, limitPerm) {
+ /** Fetches the list of funds and builds a grid from them */
+
+ function mkStore(r) {
+ var msg;
+ var items = [];
+ while(msg = r.recv()) {
+ var src = msg.content();
+ if(e = openils.Event.parse(src))
+ return alert(e);
+ openils.acq.Fund.cache[src.id()] = src;
+ items.push(src);
+ }
+ openils.acq.Fund._cachecomplete = true;
+ onComplete(acqf.toStoreData(items));
+ }
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.fund.org.retrieve'],
+ { async: true,
+ params: [openils.User.authtoken, null, {flesh_summary:1, limit_perm:limitPerm}],
+ oncomplete: mkStore
+ }
+ );
+};
+
+/**
+ * Create a new fund
+ * @param fields Key/value pairs used to create the new fund
+ */
+openils.acq.Fund.create = function(fields, onCreateComplete) {
+
+ var fund = new acqf()
+ for(var field in fields)
+ fund[field](fields[field]);
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.fund.create'],
+ { async: true,
+ params: [openils.User.authtoken, fund],
+ oncomplete: function(r) {
+ var msg = r.recv();
+ var id = msg.content();
+ if(onCreateComplete)
+ onCreateComplete(id);
+ }
+ }
+ );
+};
+
+
+openils.acq.Fund.createAllocation = function(fields, onCreateComplete) {
+ var alloc = new acqfa()
+ for(var field in fields)
+ alloc[field](fields[field]);
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.fund_allocation.create'],
+ {
+ async: true,
+ params: [openils.User.authtoken, alloc],
+ oncomplete: function(r) {
+ var msg = r.recv();
+ var id = msg.content();
+ if(onCreateComplete)
+ onCreateComplete(id);
+ }
+ }
+ );
+};
+
+
+/**
+ * Synchronous fund retrievel method
+ */
+openils.acq.Fund.retrieve = function(id) {
+ if(openils.acq.Fund.cache[id])
+ return openils.acq.Fund.cache[id];
+ openils.acq.Fund.cache[id] = fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.fund.retrieve'],
+ [openils.User.authtoken, id]
+ );
+ return openils.acq.Fund.cache[id];
+};
+
+
+openils.acq.Fund.deleteFromGrid = function(grid, onComplete) {
+ var list = []
+ var selected = grid.selection.getSelected();
+ for(var rowIdx in selected)
+ list.push(grid.model.getDatum(selected[rowIdx], 0));
+ openils.acq.Fund.deleteList(list, onComplete);
+};
+
+openils.acq.Fund.deleteList = function(list, onComplete) {
+ openils.acq.Fund._deleteList(list, 0, onComplete);
+}
+
+openils.acq.Fund._deleteList = function(list, idx, onComplete) {
+ if(idx >= list.length)
+ return onComplete();
+
+ var fundId = list[idx];
+ delete openils.acq.Fund.cache[list[idx]];
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.fund.delete'],
+ { async: true,
+ params: [openils.User.authtoken, fundId],
+ oncomplete: function(r) {
+ stat = r.recv().content();
+ /* XXX CHECH FOR EVENT */
+ openils.acq.Fund._deleteList(list, ++idx, onComplete);
+ }
+ }
+ );
+};
+
+openils.acq.Fund.nameMapping = function(oncomplete) {
+ var ids = [];
+ var names = [];
+ var buildMap = function() {
+ for (var i in openils.acq.Fund.cache) {
+ var item = openils.acq.Fund.cache[i];
+ ids.push(item.id());
+ names.push(item.name());
+ oncomplete(ids, names);
+ }
+ };
+
+ if (openils.acq.Fund._cachecomplete) {
+ buildMap(oncomplete);
+ } else {
+ openils.acq.Fund.createStore(buildMap);
+ }
+};
+
+/**
+ * Sets the store for an existing openils.widget.FundFilteringSelect
+ * using the funds where the user has the requested permission.
+ * @param perm The permission to check
+ * @param selector The pre-created dijit.form.FilteringSelect object.
+ */
+
+openils.acq.Fund.storeCache = [];
+
+openils.acq.Fund.buildPermFundSelector = function(perm, selector) {
+ dojo.require('dojo.data.ItemFileReadStore');
+
+ function hookupStore(store) {
+ selector.store = store;
+ selector.startup();
+ }
+
+ function buildPicker(r) {
+ var msg;
+ var fundList = [];
+ while (msg = r.recv()) {
+ var fund = msg.content();
+ fundList.push(fund);
+ }
+
+ var store = new dojo.data.ItemFileReadStore({data:acqf.toStoreData(fundList)});
+
+ hookupStore(store);
+ openils.acq.Fund.storeCache[perm] = store;
+ }
+
+ if (openils.acq.Fund.storeCache[perm]) {
+ hookupStore(openils.acq.Fund.storeCache[perm]);
+ } else {
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.fund.org.retrieve'],
+ { params: [openils.User.authtoken, null,
+ {flesh_summary:1, limit_perm:perm}],
+ oncomplete: buildPicker,
+ async: true
+ }
+ )
+ }
+}
+
+}
--- /dev/null
+/* ---------------------------------------------------------------------------
+ * Copyright (C) 2008 Georgia Public Library Service
+ * Bill Erickson <erickson@esilibrary.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * ---------------------------------------------------------------------------
+ */
+
+if(!dojo._hasResource['openils.acq.FundingSource']) {
+dojo._hasResource['openils.acq.FundingSource'] = true;
+dojo.provide('openils.acq.FundingSource');
+dojo.require('fieldmapper.Fieldmapper');
+dojo.require('fieldmapper.dojoData');
+
+/** Declare the FundingSource class with dojo */
+dojo.declare('openils.acq.FundingSource', null, {
+ /* add instance methods here if necessary */
+});
+
+/** cached funding_source objects */
+openils.acq.FundingSource.cache = {};
+
+openils.acq.FundingSource.createStore = function(onComplete) {
+ /** Fetches the list of funding_sources and builds a grid from them */
+ var ses = new OpenSRF.ClientSession('open-ils.acq');
+ var req = ses.request('open-ils.acq.funding_source.org.retrieve',
+ openils.User.authtoken, null, {flesh_summary:1});
+
+ req.oncomplete = function(r) {
+ var msg
+ var items = [];
+ var src = null;
+ while(msg = r.recv()) {
+ src = msg.content();
+ openils.acq.FundingSource.cache[src.id()] = src;
+ items.push(src);
+ }
+ onComplete(acqfs.toStoreData(items));
+ };
+
+ req.send();
+};
+
+
+
+/**
+ * Create a new funding source object
+ * @param fields Key/value pairs used to create the new funding source
+ */
+openils.acq.FundingSource.create = function(fields, onCreateComplete) {
+
+ var fs = new acqfs()
+ for(var field in fields)
+ fs[field](fields[field]);
+
+ var ses = new OpenSRF.ClientSession('open-ils.acq');
+ var req = ses.request('open-ils.acq.funding_source.create', openils.User.authtoken, fs);
+
+ req.oncomplete = function(r) {
+ var msg = r.recv();
+ var id = msg.content();
+ if(onCreateComplete)
+ onCreateComplete(id);
+ };
+ req.send();
+};
+
+/**
+ * Synchronous funding_source retrievel method
+ */
+openils.acq.FundingSource.retrieve = function(id) {
+ if(openils.acq.FundingSource.cache[id])
+ return openils.acq.FundingSource.cache[id];
+ openils.acq.FundingSource.cache[id] = fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.funding_source.retrieve'],
+ [openils.User.authtoken, id]
+ );
+ return openils.acq.FundingSource.cache[id];
+};
+
+
+openils.acq.FundingSource.createCredit = function(fields, onCreateComplete) {
+
+ var fsc = new acqfscred()
+ for(var field in fields)
+ fsc[field](fields[field]);
+
+ var ses = new OpenSRF.ClientSession('open-ils.acq');
+ var req = ses.request(
+ 'open-ils.acq.funding_source_credit.create', openils.User.authtoken, fsc);
+
+ req.oncomplete = function(r) {
+ var msg = r.recv();
+ var id = msg.content();
+ if(onCreateComplete)
+ onCreateComplete(id);
+ };
+ req.send();
+};
+
+
+openils.acq.FundingSource.deleteFromGrid = function(grid, onComplete) {
+ var list = []
+ var selected = grid.selection.getSelected();
+ for(var rowIdx in selected)
+ list.push(grid.model.getDatum(selected[rowIdx], 0));
+ openils.acq.FundingSource.deleteList(list, onComplete);
+};
+
+openils.acq.FundingSource.deleteList = function(list, onComplete) {
+ openils.acq.FundingSource._deleteList(list, 0, onComplete);
+}
+
+openils.acq.FundingSource._deleteList = function(list, idx, onComplete) {
+ if(idx >= list.length)
+ return onComplete();
+
+ var ses = new OpenSRF.ClientSession('open-ils.acq');
+ var req = ses.request('open-ils.acq.funding_source.delete', openils.User.authtoken, list[idx]);
+ delete openils.acq.FundingSource.cache[list[idx]];
+
+ req.oncomplete = function(r) {
+ msg = r.recv()
+ stat = msg.content();
+ /* XXX CHECH FOR EVENT */
+ openils.acq.FundingSource._deleteList(list, ++idx, onComplete);
+ }
+ req.send();
+};
+
+
+} /* end dojo._hasResource[] */
--- /dev/null
+/* ---------------------------------------------------------------------------
+ * Copyright (C) 2008 Georgia Public Library Service
+ * David J. Fiander <david@fiander.info>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * ---------------------------------------------------------------------------
+ */
+
+if(!dojo._hasResource['openils.acq.Lineitem']) {
+dojo._hasResource['openils.acq.Lineitem'] = true;
+dojo.provide('openils.acq.Lineitem');
+
+dojo.require('dojo.data.ItemFileWriteStore');
+dojo.require('dojox.grid.Grid');
+dojo.require('dojox.grid._data.model');
+dojo.require('fieldmapper.dojoData');
+dojo.require('openils.User');
+dojo.require('openils.Event');
+
+/** Declare the Lineitem class with dojo */
+dojo.declare('openils.acq.Lineitem', null, {
+ /* add instance methods here if necessary */
+
+ constructor: function(args) {
+ this.lineitem = args.lineitem;
+ },
+
+ findAttr: function(name, type) {
+ var attrs = this.lineitem.attributes();
+ if(!attrs) return null;
+ for(var i = 0; i < attrs.length; i++) {
+ var attr = attrs[i];
+ if (attr.attr_type() == type && attr.attr_name() == name)
+ return attr.attr_value();
+ }
+ },
+
+ // returns the actual price if available, otherwise estimated price, otherwise null
+ // priority is given to local attrs, then provider attrs, then MARC attrs
+ getPrice: function() {
+ return this.getActualPrice() || this.getEstimatedPrice();
+ },
+
+ // returns the actual price, null if none
+ getActualPrice : function() {
+ return this._getPriceAttr('actual_price');
+ },
+
+ // returns the estimated price, null if none
+ getEstimatedPrice : function() {
+ return this._getPriceAttr('estimated_price');
+ },
+
+ _getPriceAttr : function(attr) {
+ var types = [
+ 'lineitem_local_attr_definition',
+ 'lineitem_provider_attr_definition',
+ 'lineitem_marc_attr_definition'
+ ];
+
+ for(var t in types) {
+ if(price = this.findAttr(attr, types[t]))
+ return {price:price, source_type: attr, source_attr: types[t]};
+ }
+
+ return null;
+ },
+
+ update: function(oncomplete) {
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem.update'],
+ { async: true,
+ params: [openils.User.authtoken, this.lineitem],
+ oncomplete: function(r) {
+ oncomplete(openils.Event.parse(r.recv().content()));
+ }
+ }
+ );
+ },
+
+ approve: function(oncomplete) {
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem.approve'],
+ { async: true,
+ params: [openils.User.authtoken, this.lineitem.id()],
+ oncomplete: function(r) {
+ oncomplete(openils.Event.parse(r.recv().content()));
+ }
+ });
+ },
+
+ id: function() {
+ return this.lineitem.id();
+ },
+});
+
+openils.acq.Lineitem.ModelCache = {};
+openils.acq.Lineitem.acqlidCache = {};
+
+openils.acq.Lineitem.createLIDStore = function(li_id, onComplete) {
+ // Fetches the details of a lineitem and builds a grid
+
+ function mkStore(r) {
+ var msg;
+ var items = [];
+ while (msg = r.recv()) {
+ var data = msg.content();
+ for (i in data.lineitem_details()) {
+ var lid = data.lineitem_details()[i];
+ items.push(lid);
+ openils.acq.Lineitem.acqlidCache[lid.id()] = lid;
+ }
+ }
+
+ onComplete(acqlid.toStoreData(items));
+ }
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem.retrieve'],
+ { async: true,
+ params: [openils.User.authtoken, li_id,
+ {flesh_attrs:1, flesh_li_details:1}],
+ oncomplete: mkStore
+ });
+};
+
+openils.acq.Lineitem.alertOnLIDSet = function(griditem, attr, oldVal, newVal) {
+ var item;
+ var updateDone = function(r) {
+ var stat = r.recv().content();
+ var evt = openils.Event.parse(stat);
+
+ if (evt) {
+ alert("Error: "+evt.desc);
+ console.dir(evt);
+ if (attr == "fund") {
+ item.fund(oldVal);
+ griditem.fund = oldVal;
+ } else if (attr == "owning_lib") {
+ item.owning_lib(oldVal);
+ griditem.owning_lib = oldVal;
+ }
+ }
+ };
+
+ if (oldVal == newVal) {
+ return;
+ }
+
+ item = openils.acq.Lineitem.acqlidCache[griditem.id];
+
+ if (attr == "fund") {
+ item.fund(newVal);
+ } else if (attr == "owning_lib") {
+ item.owning_lib(newVal);
+ } else if (attr == "cn_label") {
+ item.cn_label(newVal);
+ } else if (attr == "barcode") {
+ item.barcode(newVal);
+ } else if (attr == "location") {
+ item.location(newVal);
+ } else {
+ alert("Unexpected attr in Lineitem.alertOnSet: '"+attr+"'");
+ return;
+ }
+
+ fieldmapper.standardRequest(
+ ["open-ils.acq", "open-ils.acq.lineitem_detail.update"],
+ { params: [openils.User.authtoken, item],
+ oncomplete: updateDone
+ });
+};
+
+openils.acq.Lineitem.deleteLID = function(id, onComplete) {
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem_detail.delete'],
+ { async: true,
+ params: [openils.User.authtoken, id],
+ oncomplete: function(r) {
+ msg = r.recv()
+ stat = msg.content();
+ onComplete(openils.Event.parse(stat));
+ }
+ });
+};
+
+openils.acq.Lineitem.createLID = function(fields, onCreateComplete) {
+ var lid = new acqlid()
+ for (var field in fields) {
+ lid[field](fields[field]);
+ }
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem_detail.create'],
+ { async: true,
+ params: [openils.User.authtoken, lid, {return_obj:1}],
+ oncomplete: function(r) {
+ var msg = r.recv();
+ var obj = msg.content();
+ openils.Event.parse_and_raise(obj);
+ if (onCreateComplete) {
+ onCreateComplete(obj);
+ }
+ }
+ });
+};
+
+openils.acq.Lineitem.loadLIDGrid = function(domNode, id, layout) {
+ if (!openils.acq.Lineitem.ModelCache[id]) {
+ openils.acq.Lineitem.createLIDStore(id,
+ function(storeData) {
+ var store = new dojo.data.ItemFileWriteStore({data:storeData});
+ var model = new dojox.grid.data.DojoData(null, store,
+ {rowsPerPage: 20, clientSort:true, query:{id:'*'}});
+
+ dojo.connect(store, "onSet",
+ openils.acq.Lineitem.alertOnLIDSet);
+ openils.acq.Lineitem.ModelCache[id] = model;
+
+ domNode.setStructure(layout);
+ domNode.setModel(model);
+ domNode.update();
+ });
+ } else {
+ domNode.setModel(openils.acq.Lineitem.ModelCache[id]);
+ domNode.setStructure(layout);
+ domNode.update();
+ domNode.refresh();
+ }
+};
+}
--- /dev/null
+/* ---------------------------------------------------------------------------
+ * Copyright (C) 2008 Georgia Public Library Service
+ * Bill Erickson <erickson@esilibrary.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * ---------------------------------------------------------------------------
+ */
+if(!dojo._hasResource['openils.acq.LineitemAttr']) {
+dojo._hasResource['openils.acq.LineitemAttr'] = true;
+dojo.provide('openils.acq.LineitemAttr');
+dojo.require('fieldmapper.dojoData');
+dojo.require('openils.User');
+dojo.require('openils.Event');
+
+/** Declare the LineitemAttr class with dojo */
+dojo.declare('openils.acq.LineitemAttr', null, {});
+
+/** Pile of static methods for handling the different types of
+ * lineitem attributes and definitions
+ */
+
+
+/**
+ * Creates a set of attr definition stores, one per definition type.
+ */
+openils.acq.LineitemAttr.createAttrDefStores = function(onload) {
+ function process(r) {
+ var res = r.recv().content();
+ openils.Event.parse_and_raise(res);
+ var stores = {};
+ stores.marc = acqlimad.toStoreData(res.marc);
+ stores.usr = acqliuad.toStoreData(res.usr);
+ stores.local = acqlilad.toStoreData(res.local);
+ stores.generated = acqligad.toStoreData(res.generated);
+ stores.provider = acqlipad.toStoreData(res.provider);
+ onload(stores);
+ }
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem_attr_definition.retrieve.all'],
+ { async: true,
+ params: [openils.User.authtoken],
+ oncomplete: process
+ }
+ );
+}
+}
--- /dev/null
+/* ---------------------------------------------------------------------------
+ * Copyright (C) 2008 Georgia Public Library Service
+ * Bill Erickson <erickson@esilibrary.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * ---------------------------------------------------------------------------
+ */
+
+if(!dojo._hasResource['openils.acq.PO']) {
+
+ dojo._hasResource['openils.acq.PO'] = true;
+ dojo.provide('openils.acq.PO');
+ dojo.require('fieldmapper.Fieldmapper');
+ dojo.require('fieldmapper.dojoData');
+ dojo.require('openils.Util');
+
+ /** Declare the PO class with dojo */
+ dojo.declare('openils.acq.PO', null, {
+ /* add instance methods here if necessary */
+ });
+
+ openils.acq.PO.cache = {};
+
+ openils.acq.PO.retrieve = function(id, oncomplete, args) {
+
+ var req = ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'];
+ var par = [openils.User.authtoken, id, args];
+
+ if(oncomplete) {
+ fieldmapper.standardRequest(
+ req,
+ { params:par,
+ async: true,
+ oncomplete:function(r) {
+ var po = openils.Util.extractResponse(r)
+ if(po) {
+ openils.acq.PO.cache[po.id()] = po;
+ oncomplete(po);
+ }
+ }
+ }
+ );
+ } else {
+ return openils.acq.PO.cache[po.id()] =
+ fieldmapper.standardRequest(req, par);
+ }
+ }
+
+ openils.acq.PO.create = function(po, oncomplete) {
+ var req = ['open-ils.acq', 'open-ils.acq.purchase_order.create'];
+ var par = [openils.User.authtoken, po];
+
+ fieldmapper.standardRequest(
+ req,
+ { params: par,
+ async: true,
+ oncomplete: function(r) {
+ var po_id = r.recv().content();
+ po.id(po_id);
+ openils.acq.PO.cache[po_id] = po;
+ oncomplete(po_id);
+ }
+ }
+ );
+ }
+};
+
+
--- /dev/null
+/* ---------------------------------------------------------------------------
+ * Copyright (C) 2008 Georgia Public Library Service
+ * David J. Fiander <david@fiander.info>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * ---------------------------------------------------------------------------
+ */
+
+if(!dojo._hasResource['openils.acq.Picklist']) {
+dojo._hasResource['openils.acq.Picklist'] = true;
+dojo.provide('openils.acq.Picklist');
+
+dojo.require('dojo.data.ItemFileWriteStore');
+dojo.require('dojox.grid.Grid');
+dojo.require('dojox.grid._data.model');
+dojo.require('fieldmapper.Fieldmapper');
+dojo.require('fieldmapper.dojoData');
+dojo.require('openils.Event');
+
+/** Declare the Picklist class with dojo */
+dojo.declare('openils.acq.Picklist', null, {
+
+ constructor: function (pl_id, onComplete, args) {
+
+ var pl_this = this; // 'this' doesn't exist inside callbacks
+ var liArgs = (args && args.liArgs) ? args.liArgs : {flesh_attrs:1, clear_marc:1};
+ var mkStore = function (r) {
+ var storeData;
+ var msg;
+ pl_this._items = [];
+
+ while (msg = r.recv()) {
+ var data = msg.content();
+ pl_this._data[data.id()] = data;
+ pl_this._items.push(data);
+ }
+
+ storeData = jub.toStoreData(pl_this._items, null, {virtualFields:['estimated_price', 'actual_price']});
+ pl_this._store = new dojo.data.ItemFileWriteStore({data:storeData});
+ pl_this._model = new dojox.grid.data.DojoData(null, pl_this._store,
+ {rowsPerPage:20, clientSort:true,
+ query:{id:'*'}});
+ onComplete(pl_this._model);
+ };
+
+ this._id = pl_id;
+ this._data = {};
+ this._plist = null;
+ //
+ // Fetch the picklist information
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.picklist.retrieve'],
+ { async: false,
+ params: [openils.User.authtoken, pl_id, {flesh_lineitem_count:1}],
+ oncomplete: function(r) {
+ var pl = r.recv().content();
+ if(e = openils.Event.parse(pl))
+ return alert(pl);
+ pl_this._plist = pl;
+ }
+ });
+
+ // Fetch the title list for the picklist, asynchronously
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem.picklist.retrieve'],
+ { async: true,
+ params: [openils.User.authtoken, pl_id, liArgs],
+ oncomplete: mkStore
+ });
+ },
+
+ id: function () {
+ return this._id;
+ },
+ name: function() {
+ return this._plist.name();
+ },
+ owner: function() {
+ return this._plist.owner();
+ },
+ create_time: function() {
+ return this._plist.create_time();
+ },
+ edit_time: function() {
+ return this._plist.edit_time();
+ },
+
+ find_attr: function(id, at_name, at_type) {
+ attr_list = this._data[id].attributes();
+ for (var i in attr_list) {
+ var attr = attr_list[i];
+ if (attr.attr_type() == at_type && attr.attr_name() == at_name) {
+ return attr.attr_value();
+ }
+ }
+ return '';
+ },
+});
+
+/** Creates a new picklist. fields.name is required */
+openils.acq.Picklist.create = function(fields, oncomplete) {
+ var picklist = new acqpl();
+ picklist.owner(fields.owner || new openils.User().user.id());
+ picklist.name(fields.name);
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.picklist.create'],
+ { async: true,
+ params: [openils.User.authtoken, picklist],
+ oncomplete: function(r) {
+ // XXX event/error handling
+ oncomplete(r.recv().content());
+ }
+ }
+ );
+}
+
+/** Creates a new picklist. fields.name is required */
+openils.acq.Picklist.update = function(picklist, oncomplete) {
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.picklist.update'],
+ { async: true,
+ params: [openils.User.authtoken, picklist],
+ oncomplete: function(r) {
+ // XXX event/error handling
+ oncomplete(r.recv().content());
+ }
+ }
+ );
+}
+
+/** Deletes a list of picklists
+ * @param list Array of picklist IDs
+ */
+openils.acq.Picklist.deleteList = function(list, onComplete) {
+ openils.acq.Picklist._deleteList(list, 0, onComplete);
+}
+
+/* iterate through the list of IDs deleting asynchronously as we go... */
+openils.acq.Picklist._deleteList = function(list, idx, onComplete) {
+ if(idx >= list.length)
+ return onComplete();
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.picklist.delete'],
+ { async: true,
+ params: [openils.User.authtoken, list[idx]],
+ oncomplete: function(r) {
+ msg = r.recv()
+ stat = msg.content();
+ /* XXX CHECH FOR EVENT */
+ openils.acq.Picklist._deleteList(list, ++idx, onComplete);
+ }
+ }
+ );
+}
+
+}
+
--- /dev/null
+/* ---------------------------------------------------------------------------
+ * Copyright (C) 2008 Georgia Public Library Service
+ * Bill Erickson <erickson@esilibrary.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * ---------------------------------------------------------------------------
+ */
+
+if(!dojo._hasResource['openils.acq.Provider']) {
+dojo._hasResource['openils.acq.Provider'] = true;
+dojo.provide('openils.acq.Provider');
+dojo.require('fieldmapper.Fieldmapper');
+dojo.require('fieldmapper.dojoData');
+
+/** Declare the Provider class with dojo */
+dojo.declare('openils.acq.Provider', null, {
+ /* add instance methods here if necessary */
+});
+
+openils.acq.Provider.cache = {};
+
+/* define some static provider methods ------- */
+
+openils.acq.Provider.createStore = function(onComplete, limitPerm) {
+ /** Fetches the list of funding_sources and builds a grid from them */
+
+ function mkStore(r) {
+ var msg;
+ var items = [];
+ while(msg = r.recv()) {
+ var provider = msg.content();
+ openils.acq.Provider.cache[provider.id()] = provider;
+ items.push(provider);
+ }
+ onComplete(acqpro.toStoreData(items));
+ }
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.provider.org.retrieve'],
+ { async: true,
+ params: [openils.User.authtoken],
+ oncomplete: mkStore
+ }
+ );
+};
+
+
+/**
+ * Synchronous provider retrievel method
+ */
+openils.acq.Provider.retrieve = function(id) {
+ if(openils.acq.Provider.cache[id])
+ return openils.acq.Provider.cache[id];
+
+ openils.acq.Provider.cache[id] =
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.provider.retrieve'],
+ [openils.User.authtoken, id]
+ );
+ return openils.acq.Provider.cache[id];
+};
+
+openils.acq.Provider.create = function(fields, oncomplete) {
+ var provider = new acqpro()
+ for(var field in fields)
+ provider[field](fields[field]);
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.provider.create'],
+ { async: true,
+ params: [openils.User.authtoken, provider],
+ oncomplete: function(r) {
+ var msg = r.recv();
+ var id = msg.content();
+ if(oncomplete)
+ oncomplete(id);
+ }
+ }
+ );
+};
+
+
+openils.acq.Provider.retrieveLineitemProviderAttrDefs = function(providerId, oncomplete) {
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem_provider_attr_definition.provider.retrieve.atomic'],
+ { async: true,
+ params: [openils.User.authtoken, providerId],
+ oncomplete: function(r) {oncomplete(r.recv().content());}
+ }
+ );
+}
+
+openils.acq.Provider.createLineitemProviderAttrDef = function(fields, oncomplete) {
+ var attr = new acqlipad();
+ for(var field in fields)
+ attr[field](fields[field]);
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem_provider_attr_definition.create'],
+ { async: true,
+ params: [openils.User.authtoken, attr],
+ oncomplete: function(r) {oncomplete(r.recv().content());}
+ }
+ );
+}
+
+
+openils.acq.Provider.lineitemProviderAttrDefDeleteList = function(list, oncomplete) {
+ openils.acq.Provider._lineitemProviderAttrDefDeleteList(list, 0, oncomplete);
+}
+
+openils.acq.Provider._lineitemProviderAttrDefDeleteList = function(list, idx, oncomplete) {
+ if(idx >= list.length)
+ return oncomplete();
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem_provider_attr_definition.delete'],
+ { async: true,
+ params: [openils.User.authtoken, list[idx]],
+ oncomplete: function(r) {
+ msg = r.recv()
+ stat = msg.content();
+ /* XXX CHECH FOR EVENT */
+ openils.acq.Provider._lineitemProviderAttrDefDeleteList(list, ++idx, oncomplete);
+ }
+ }
+ );
+}
+
+openils.acq.Provider.storeCache = [];
+
+openils.acq.Provider.buildPermProviderSelector = function(perm, selector) {
+ dojo.require('dojo.data.ItemFileReadStore');
+
+ function hookupStore(store) {
+ selector.store = store;
+ selector.startup();
+ }
+
+ function buildPicker(r) {
+ var msg;
+ var providerList = [];
+ while (msg = r.recv()) {
+ var provider = msg.content();
+ providerList.push(provider);
+ }
+
+ var store = new dojo.data.ItemFileReadStore({data:acqpro.toStoreData(providerList)});
+
+ hookupStore(store);
+ openils.acq.Provider.storeCache[perm] = store;
+ }
+
+ if (openils.acq.Provider.storeCache[perm]) {
+ hookupStore(openils.acq.Provider.storeCache[perm]);
+ } else {
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.provider.org.retrieve'],
+ { params: [openils.User.authtoken, null,
+ {flesh_summary:1, limit_perm:perm}],
+ oncomplete: buildPicker,
+ async: true
+ }
+ )
+ }
+}
+}
--- /dev/null
+if(!dojo._hasResource["openils.editors"]){
+dojo._hasResource["openils.editors"] = true;
+dojo.provide("openils.editors");
+
+dojo.require("dojox.grid._data.dijitEditors");
+dojo.require("dojox.grid._data.editors");
+dojo.require("dijit.form.NumberSpinner");
+dojo.require('dijit.form.FilteringSelect');
+
+dojo.require("openils.widget.FundSelector");
+dojo.require("openils.widget.ProviderSelector");
+dojo.require("openils.widget.OrgUnitFilteringSelect");
+
+dojo.require("openils.acq.Fund");
+
+dojo.declare("openils.editors.NumberSpinner", dojox.grid.editors.Dijit, {
+ editorClass: "dijit.form.NumberSpinner",
+
+ getvalue: function() {
+ var e = this.editor;
+ // make sure to apply the displayed value
+ e.setDisplayedValue(e.getDisplayedValue());
+ return e.getValue();
+ },
+
+ getEditorProps: function(inDatum){
+ return dojo.mixin({}, this.cell.editorProps||{}, {
+ constraints: dojo.mixin({}, this.cell.constraints) || {},
+ value: inDatum
+ });
+ },
+});
+
+dojo.declare('openils.editors.FundSelectEditor', dojox.grid.editors.Dijit, {
+ editorClass: "openils.widget.FundSelector",
+ createEditor: function(inNode, inDatum, inRowIndex) {
+ var editor = new this.editorClass(this.getEditorProps(inDatum), inNode);
+ openils.acq.Fund.buildPermFundSelector(this.cell.perm || this.perm,
+ editor);
+ return editor;
+ },
+});
+
+dojo.declare('openils.editors.ProviderSelectEditor', dojox.grid.editors.Dijit, {
+ editorClass: "openils.widget.ProviderSelector",
+ createEditor: function(inNode, inDatum, inRowIndex) {
+ console.log("openils.widget.ProviderSelectEditor");
+ var editor = new this.editorClass(this.getEditorProps(inDatum), inNode);
+ openils.acq.Provider.buildPermProviderSelector(this.cell.perm || this.perm,
+ editor);
+ return editor;
+ },
+});
+
+dojo.declare('openils.editors.OrgUnitSelectEditor', dojox.grid.editors.Dijit, {
+ editorClass: "openils.widget.OrgUnitFilteringSelect",
+ createEditor: function(inNode, inDatum, inRowIndex) {
+ var editor = new this.editorClass(this.getEditorProps(inDatum), inNode);
+ new openils.User().buildPermOrgSelector(this.cell.perm || this.perm, editor);
+ editor.setValue(inDatum);
+ return editor;
+ },
+});
+
+dojo.declare('openils.editors.CopyLocationSelectEditor', dojox.grid.editors.Dijit, {
+ editorClass: "dijit.form.FilteringSelect",
+ createEditor: function(inNode, inDatum, inRowIndex) {
+ dojo.require('openils.CopyLocation');
+ var editor = new this.editorClass(this.getEditorProps(inDatum), inNode);
+ openils.CopyLocation.createStore(1, /* XXX how do we propagate arguments to the editor?? */
+ function(store) {
+ editor.store = new dojo.data.ItemFileReadStore({data:store});
+ editor.startup();
+ if(inDatum)
+ editor.setValue(inDatum);
+ }
+ );
+ return editor;
+ },
+});
+
+}
+
--- /dev/null
+if(!dojo._hasResource["openils.widget.FundSelector"]){
+ dojo._hasResource["openils.widget.FundSelector"] = true;
+ dojo.provide("openils.widget.FundSelector");
+
+ dojo.require('openils.acq.Fund');
+ dojo.require('fieldmapper.Fieldmapper');
+ dojo.require('fieldmapper.dojoData');
+
+ /**
+ * This widget provides a specific selector for selecting
+ * a fund.
+ */
+
+ dojo.declare(
+ "openils.widget.FundSelector", [dijit.form.FilteringSelect],
+ {
+ });
+}
--- /dev/null
+if(!dojo._hasResource["openils.widget.ProviderSelector"]){
+ dojo._hasResource["openils.widget.ProviderSelector"] = true;
+ dojo.provide("openils.widget.ProviderSelector");
+
+ dojo.require('openils.acq.Provider');
+ dojo.require('fieldmapper.Fieldmapper');
+ dojo.require('fieldmapper.dojoData');
+
+ /**
+ * This widget provides a specific selector for selecting
+ * a provider.
+ */
+
+ dojo.declare(
+ "openils.widget.ProviderSelector", [dijit.form.FilteringSelect],
+ {
+ labelAttr: 'code',
+ searchAttr: 'code'
+ });
+}
--- /dev/null
+dojo.require('dijit.Dialog');
+dojo.require('fieldmapper.dojoData');
+dojo.require('openils.User');
+dojo.require('dojo.cookie');
+dojo.require('openils.CGI');
+dojo.require('openils.Event');
+
+function oilsSetupUser() {
+ var authtoken = new openils.CGI().param('ses') || dojo.cookie('ses');
+ var user;
+ if(authtoken) user = new openils.User({authtoken:authtoken});
+ if(!authtoken || openils.Event.parse(user.user)) {
+ dojo.cookie('ses', openils.User.authtoken, {expires:-1, path:'/'});
+ openils.User.authtoken = null;
+ dojo.addOnLoad(function(){oilsLoginDialog.show();});
+ return;
+ }
+ dojo.cookie('ses', authtoken, {path : oilsCookieBase});
+ openils.User.authtoken = authtoken;
+}
+
+function oilsDoLogin() {
+ var user = new openils.User();
+ user.login({
+ username: dojo.byId('oils-login-username').value,
+ passwd: dojo.byId('oils-login-password').value,
+ type: 'staff' // hardcode for now
+ });
+ dojo.cookie('ses', user.authtoken, {path : oilsCookieBase});
+ return true;
+}
+
+oilsSetupUser();
+
--- /dev/null
+dojo.require('dojo.data.ItemFileReadStore');
+dojo.require('dijit.layout.SplitContainer');
+dojo.require('dijit.Dialog');
+dojo.require('dijit.form.FilteringSelect');
+dojo.require('dijit.form.Button');
+dojo.require('dojox.grid.Grid');
+dojo.require('dojo.date.locale');
+dojo.require('dojo.date.stamp');
+
+
+dojo.require("openils.User");
+dojo.require("openils.acq.Fund");
+dojo.require("openils.acq.Lineitem");
+dojo.require('openils.acq.Provider');
+dojo.require("openils.widget.FundSelector");
+dojo.require('openils.editors');
+dojo.require('openils.Event');
+dojo.require("openils.widget.OrgUnitFilteringSelect");
+dojo.require("fieldmapper.OrgUtils");
+
+/* put all the accessors, etc. into a local object for namespacing */
+var JUBGrid = {
+ jubGrid : null,
+ lineitems : [], // full list of lineitem objects to display
+ getLi : function(id) {
+ // given an ID, returns the lineitem object from the list
+ for(var i in JUBGrid.lineitems) {
+ var li = JUBGrid.lineitems[i];
+ if(li.id() == id)
+ return li;
+ }
+ },
+
+ _getMARCAttr : function(rowIndex, attr) {
+ var data = JUBGrid.jubGrid.model.getRow(rowIndex);
+ if (!data) return '';
+ return new openils.acq.Lineitem(
+ {lineitem:JUBGrid.getLi(data.id)}).findAttr(attr, 'lineitem_marc_attr_definition')
+ },
+ getJUBTitle : function(rowIndex) {
+ return JUBGrid._getMARCAttr(rowIndex, 'title');
+ },
+ getJUBAuthor : function(rowIndex) {
+ return JUBGrid._getMARCAttr(rowIndex, 'author');
+ },
+ getJUBIsbn : function(rowIndex) {
+ return JUBGrid._getMARCAttr(rowIndex, 'isbn');
+ },
+ getJUBActualPrice : function(rowIndex) {
+ var data = JUBGrid.jubGrid.model.getRow(rowIndex);
+ if (!data) return '';
+ var price = new openils.acq.Lineitem(
+ {lineitem:JUBGrid.getLi(data.id)}).getActualPrice();
+ if(price) return price.price;
+ return ''
+ },
+ getJUBEstimatedPrice : function(rowIndex) {
+ var data = JUBGrid.jubGrid.model.getRow(rowIndex);
+ if (!data) return '';
+ var price = new openils.acq.Lineitem(
+ {lineitem:JUBGrid.getLi(data.id)}).getEstimatedPrice();
+ if(price) return price.price;
+ return ''
+ },
+ getJUBPubdate : function(rowIndex) {
+ return JUBGrid._getMARCAttr(rowIndex, 'pubdate');
+ },
+ getProvider : function(rowIndex) {
+ data = JUBGrid.jubGrid.model.getRow(rowIndex);
+ if(!data || !data.provider) return;
+ return openils.acq.Provider.retrieve(data.provider).code();
+ },
+ getRecvTime : function(rowIndex) {
+ var data = JUBGrid.jubDetailGrid.model.getRow(rowIndex);
+ if (!(data && data.recv_time)) return '';
+ var date = dojo.date.stamp.fromISOString(data.recv_time);
+ return dojo.date.locale.format(date, {formatLength:'medium'});
+ },
+ getCopyLocation : function(rowIndex) {
+ var data = JUBGrid.jubDetailGrid.model.getRow(rowIndex);
+ if(!data || !data.location) return '';
+ return openils.CopyLocation.retrieve(data.location).name();
+ },
+ getLIDFundName : function(rowIndex) {
+ var data = JUBGrid.jubDetailGrid.model.getRow(rowIndex);
+ if (!data || !data.fund) return;
+ try {
+ return openils.acq.Fund.retrieve(data.fund).name();
+ } catch (evt) {
+ return data.fund;
+ }
+ },
+ getLIDFundCode : function(rowIndex) {
+ var data = JUBGrid.jubDetailGrid.model.getRow(rowIndex);
+ if (!data || !data.fund) return;
+ try {
+ return openils.acq.Fund.retrieve(data.fund).code();
+ } catch (evt) {
+ return data.fund;
+ }
+ },
+ getLIDLibName : function(rowIndex) {
+ var data = JUBGrid.jubDetailGrid.model.getRow(rowIndex);
+ if (!data || !data.owning_lib) return;
+ return fieldmapper.aou.findOrgUnit(data.owning_lib).shortname();
+ },
+
+ gridDataChanged : function(newVal, rowIdx, cellIdx) {
+ // cellIdx == -1 if you are editing a column that
+ // is not represented in the data model. Khaaaaaaan!!!
+ },
+
+ populate : function(gridWidget, model, lineitems) {
+ for (var i in lineitems) {
+ JUBGrid.lineitems[lineitems[i].id()] = lineitems[i];
+ }
+ JUBGrid.jubGrid = gridWidget;
+ JUBGrid.jubGrid.setModel(model);
+ if(JUBGrid.showDetails) {
+ dojo.connect(gridWidget, "onRowClick",
+ function(evt) {
+ var jub = model.getRow(evt.rowIndex);
+ var grid;
+
+ JUBGrid.jubDetailGrid.lineitemID = jub.id;
+
+ //if (jub.state == "approved") {
+ if (false) { // need finer grained control here
+ grid = JUBGrid.jubDetailGridLayoutReadOnly;
+ } else {
+ grid = JUBGrid.jubDetailGridLayout;
+ }
+ openils.acq.Lineitem.loadLIDGrid(
+ JUBGrid.jubDetailGrid,
+ JUBGrid.jubGrid.model.getRow(evt.rowIndex).id, grid);
+ }
+ );
+ }
+ // capture changes to lineitems
+ dojo.connect(model.store, "onSet", JUBGrid.onJUBSet);
+ gridWidget.update();
+ },
+
+ approveJUB: function(evt) {
+ var list = [];
+ var selected = JUBGrid.jubGrid.selection.getSelected();
+ for (var idx = 0; idx < selected.length; idx++) {
+ var rowIdx = selected[idx];
+ JUBGrid.approveSingleJUB(JUBGrid.jubGrid.model.getRow(rowIdx));
+ }
+ },
+
+ approveSingleJUB: function(jub) {
+ var li = new openils.acq.Lineitem({lineitem:JUBGrid.getLi(jub.id)});
+ var approveStore = function(evt) {
+ if (evt) {
+ // something bad happened
+ console.log("jubgrid.js: approveJUB: error:");
+ console.dir(evt);
+ alert("Error: "+evt.desc);
+ } else {
+ var approveACQLI = function(jub, rq) {
+ JUBGrid.jubGrid.model.store.setValue(jub, "state", "approved");
+ JUBGrid.jubGrid.model.refresh();
+ JUBGrid.jubGrid.update();
+ // Reload lineitem details, read-only
+ //openils.acq.Lineitem.loadLIDGrid(JUBGrid.jubDetailGrid, li.id(), JUBGrid.jubDetailGridLayout);
+ //JUBGrid.jubDetailGridLayoutReadOnly);
+ };
+ JUBGrid.jubGrid.model.store.fetch({query:{id:jub.id}, onItem: approveACQLI});
+ }
+ };
+
+ li.approve(approveStore);
+ },
+
+
+ removeSelectedJUBs: function(evt) {
+
+ function deleteList(list, idx, oncomplete) {
+ if(idx >= list.length)
+ return oncomplete();
+ fieldmapper.standardRequest([
+ 'open-ils.acq',
+ 'open-ils.acq.lineitem.delete'],
+ { async: true,
+ params: [openils.User.authtoken, list[idx].id()],
+ oncomplete: function(r) {
+ var res = r.recv().content();
+ if(openils.Event.parse(res))
+ alert(openils.Event.parse(res));
+ deleteList(list, ++idx, oncomplete);
+ }
+ }
+ );
+ }
+
+ var lineitems = JUBGrid.lineitems;
+ var deleteMe = [];
+ var keepMe = [];
+ var selected = JUBGrid.jubGrid.selection.getSelected();
+
+ for(var id in lineitems) {
+ var deleted = false;
+ for(var i = 0; i < selected.length; i++) {
+ var rowIdx = selected[i];
+ var jubid = JUBGrid.jubGrid.model.getRow(rowIdx).id;
+ if(jubid == id) {
+ if (lineitems[id].state() == 'new') {
+ deleteMe.push(lineitems[id]);
+ deleted = true;
+ } else {
+ alert("Can not delete line item "+id+
+ ": item is "+lineitems[id].state());
+ }
+ }
+ }
+ if(!deleted)
+ keepMe[id] = lineitems[id];
+ }
+
+ JUBGrid.lineitems = keepMe;
+ deleteList(deleteMe, 0, function(){
+ JUBGrid.jubGrid.model.store =
+ new dojo.data.ItemFileReadStore({data:jub.toStoreData(keepMe)});
+ JUBGrid.jubGrid.model.refresh();
+ JUBGrid.jubGrid.update();
+ });
+ },
+
+ deleteLID: function(evt) {
+ var list =[];
+ var selected = JUBGrid.jubDetailGrid.selection.getSelected();
+ for (var idx = 0; idx < selected.length; idx++) {
+ var rowIdx = selected[idx];
+ var lid = JUBGrid.jubDetailGrid.model.getRow(rowIdx);
+ var deleteFromStore = function (evt) {
+
+ if (evt) {
+ // something bad happened
+ alert("Error: "+evt.desc);
+ } else {
+ var deleteItem = function(item, rq) {
+ JUBGrid.jubDetailGrid.model.store.deleteItem(item);
+ };
+ var updateCount = function(item) {
+ var newval = JUBGrid.jubGrid.model.store.getValue(item, "item_count");
+ JUBGrid.jubGrid.model.store.setValue(item, "item_count", newval-1);
+ JUBGrid.jubGrid.update();
+ };
+
+ JUBGrid.jubDetailGrid.model.store.fetch({query:{id:lid.id},
+ onItem: deleteItem});
+ JUBGrid.jubGrid.model.store.fetch({query:{id:JUBGrid.jubDetailGrid.lineitemID},
+ onItem: updateCount});
+ }
+ JUBGrid.jubDetailGrid.update();
+ };
+
+ openils.acq.Lineitem.deleteLID(lid.id, deleteFromStore);
+ }
+ },
+
+ createLID: function(fields) {
+ fields['lineitem'] = JUBGrid.jubDetailGrid.lineitemID;
+ var addToStore = function (lid) {
+ JUBGrid.jubDetailGrid.model.store.newItem(acqlid.toStoreData([lid]).items[0]);
+ JUBGrid.jubDetailGrid.refresh();
+ JUBGrid.jubGrid.update();
+ JUBGrid.jubGrid.refresh();
+ }
+ openils.acq.Lineitem.createLID(fields, addToStore);
+ },
+
+ receiveLID: function(evt) {
+ var list =[];
+ var selected = JUBGrid.jubDetailGrid.selection.getSelected();
+ for (var idx = 0; idx < selected.length; idx++) {
+ var rowIdx = selected[idx];
+ var lid = JUBGrid.jubDetailGrid.model.getRow(rowIdx);
+ list.push(lid.id);
+ }
+ if(lid != null) { // is at least one selected?
+ JUBGrid._receiveLIDList(list, 0,
+ function() {
+ delete openils.acq.Lineitem.ModelCache[lid.lineitem];
+ openils.acq.Lineitem.loadLIDGrid(
+ JUBGrid.jubDetailGrid, lid.lineitem, JUBGrid.jubDetailGridLayout);
+ }
+ );
+ }
+ },
+
+ // loop through the list of LIDs and mark them as received
+ _receiveLIDList: function(list, idx, callback) {
+ if(idx >= list.length)
+ return callback();
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem_detail.receive'],
+ { asnync: true,
+ params: [openils.User.authtoken, list[idx++]],
+ oncomplete: function(r) {
+ var res = r.recv().content();
+ if(e = openils.Event.parse(res))
+ return alert(e);
+ JUBGrid._receiveLIDList(list, idx, callback);
+ }
+ }
+ );
+ },
+
+
+ // called when a lineitem is edited
+ onJUBSet: function (griditem, attr, oldVal,newVal) {
+ var item;
+
+ var updateDone = function(r) {
+ var stat = r.recv().content();
+ if(e = openils.Event.parse(stat))
+ console.dir(e);
+ };
+
+ // after an attribute has been updated
+ var attrUpdateDone = function(r, attr) {
+ var res = r.recv().content();
+ if(e = openils.Event.parse(res))
+ return alert(e);
+
+ var oldVal = new openils.acq.Lineitem(
+ {lineitem:item}).findAttr(attr, 'lineitem_local_attr_definition');
+
+ if(oldVal) {
+ // if this attr already exists on the object, just update the value
+ for(var i = 0; i < item.attributes().length; i++) {
+ var attrobj = item.attributes()[i];
+ if(attrobj.attr_type() == 'lineitem_local_attr_definition' && attrobj.attr_name() == attr) {
+ attrobj.attr_value(newVal);
+ }
+ }
+ } else {
+ // if this is a new attribute, create a new object to match reality
+ liad = new acqlia();
+ liad.attr_type('lineitem_local_attr_definition');
+ liad.attr_value(newVal);
+ liad.attr_name(attr);
+ liad.id(res);
+ item.attributes().push(liad);
+ }
+ }
+
+ if (oldVal == newVal) {
+ return;
+ }
+
+ item = JUBGrid.lineitems[griditem.id];
+ if (attr == "provider") {
+ if(newVal == '')
+ newVal = null;
+ item.provider(newVal);
+
+ } else if(attr == 'estimated_price' || attr == 'actual_price') {
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem_local_attr.set'],
+ { async: true,
+ params: [openils.User.authtoken, item.id(), attr, newVal],
+ oncomplete: function(r) {attrUpdateDone(r, attr); }
+ }
+ );
+ } else {
+ //alert("Unexpected attr in Picklist.onSet: '"+attr+"'");
+ return;
+ }
+
+ fieldmapper.standardRequest(
+ ["open-ils.acq", "open-ils.acq.lineitem.update"],
+ {params: [openils.User.authtoken, item],
+ oncomplete: updateDone
+ }
+ );
+ },
+};
+
--- /dev/null
+dojo.require("dijit.Dialog");
+dojo.require('dijit.form.Button');
+dojo.require('dojox.grid.Grid');
+dojo.require('openils.acq.CurrencyType');
+dojo.require('openils.Event');
+dojo.require('openils.Util');
+dojo.require('fieldmapper.dojoData');
+
+var currencyTypes = [];
+
+function loadCTypesGrid() {
+ openils.acq.CurrencyType.fetchAll(
+ function(types) {
+ var store = new dojo.data.ItemFileReadStore(
+ {data:acqct.toStoreData(types, 'code', {identifier:'code'})});
+ var model = new dojox.grid.data.DojoData(null, store,
+ {rowsPerPage: 20, clientSort: true, query:{code:'*'}});
+ currencyTypeListGrid.setModel(model);
+ currencyTypeListGrid.update();
+ }
+ );
+}
+
+
+openils.Util.addOnLoad(loadCTypesGrid);
--- /dev/null
+dojo.require("dijit.Dialog");
+dojo.require("dijit.form.FilteringSelect");
+dojo.require('openils.acq.FundingSource');
+dojo.require('openils.acq.CurrencyType');
+dojo.require('openils.widget.OrgUnitFilteringSelect');
+dojo.require('dijit.form.Button');
+dojo.require('dojox.grid.Grid');
+dojo.require('openils.Event');
+dojo.require('openils.Util');
+
+function getOrgInfo(rowIndex) {
+ data = fundingSourceListGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return fieldmapper.aou.findOrgUnit(data.owner).shortname();
+}
+
+function getBalanceInfo(rowIndex) {
+ data = fundingSourceListGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return new String(openils.acq.FundingSource.cache[data.id].summary().balance);
+}
+
+function loadFSGrid() {
+ openils.acq.FundingSource.createStore(
+ function(storeData) {
+ var store = new dojo.data.ItemFileReadStore({data:storeData});
+ var model = new dojox.grid.data.DojoData(null, store,
+ {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ fundingSourceListGrid.setModel(model);
+ fundingSourceListGrid.update();
+ }
+ );
+}
+
+openils.Util.addOnLoad(loadFSGrid);
--- /dev/null
+dojo.require("dijit.Dialog");
+dojo.require("dijit.form.FilteringSelect");
+dojo.require('dijit.form.Button');
+dojo.require('dojox.grid.Grid');
+
+dojo.require('openils.widget.OrgUnitFilteringSelect');
+dojo.require('openils.acq.CurrencyType');
+dojo.require('openils.Event');
+dojo.require('openils.Util');
+dojo.require('openils.acq.Fund');
+
+function getOrgInfo(rowIndex) {
+ data = fundListGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return fieldmapper.aou.findOrgUnit(data.org).shortname();
+}
+
+function getBalanceInfo(rowIndex) {
+ data = fundListGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return new String(openils.acq.Fund.cache[data.id].summary().combined_balance);
+}
+
+
+function loadFundGrid() {
+ openils.acq.Fund.createStore(
+ function(storeData) {
+ var store = new dojo.data.ItemFileReadStore({data:storeData});
+ var model = new dojox.grid.data.DojoData(null, store,
+ {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ fundListGrid.setModel(model);
+ fundListGrid.update();
+
+ var yearStore = {identifier:'year', name:'year', items:[]};
+
+ var added = [];
+ for(var i = 0; i < storeData.items.length; i++) {
+ var year = storeData.items[i].year;
+ if(added.indexOf(year) == -1) {
+ yearStore.items.push({year:year});
+ added.push(year);
+ }
+ }
+ yearStore.items = yearStore.items.sort().reverse();
+ fundFilterYearSelect.store = new dojo.data.ItemFileReadStore({data:yearStore});
+ var today = new Date().getFullYear().toString();
+ fundFilterYearSelect.setValue((added.indexOf(today != -1)) ? today : added[0]);
+ }
+ );
+}
+
+function filterGrid() {
+ var year = fundFilterYearSelect.getValue();
+ if(year)
+ fundListGrid.model.query = {year:year};
+ else
+ fundListGrid.model.query = {id:'*'};
+ fundListGrid.model.refresh();
+ fundListGrid.update();
+}
+
+openils.Util.addOnLoad(loadFundGrid);
+
--- /dev/null
+dojo.require("dijit.Dialog");
+dojo.require("dijit.form.FilteringSelect");
+dojo.require('dijit.form.Button');
+dojo.require('dojox.grid.Grid');
+
+dojo.require('openils.acq.CurrencyType');
+dojo.require('openils.Event');
+dojo.require('openils.Util');
+dojo.require('openils.acq.Provider');
+dojo.require("fieldmapper.OrgUtils");
+dojo.require('openils.widget.OrgUnitFilteringSelect');
+
+function getOrgInfo(rowIndex) {
+ data = providerListGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return fieldmapper.aou.findOrgUnit(data.owner).shortname();
+}
+
+function loadProviderGrid() {
+ openils.acq.Provider.createStore(
+ function(storeData) {
+ var store = new dojo.data.ItemFileReadStore({data:storeData});
+ var model = new dojox.grid.data.DojoData(null, store,
+ {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ providerListGrid.setModel(model);
+ providerListGrid.update();
+ }
+ );
+}
+function createProvider(fields) {
+ openils.acq.Provider.create(fields, function(){loadProviderGrid()});
+}
+
+
+openils.Util.addOnLoad(loadProviderGrid);
--- /dev/null
+dojo.require("dijit.Dialog");
+dojo.require('dijit.form.FilteringSelect');
+dojo.require('dijit.layout.TabContainer');
+dojo.require('dijit.layout.ContentPane');
+dojo.require('dojox.grid.Grid');
+
+dojo.require("fieldmapper.OrgUtils");
+dojo.require('openils.acq.Fund');
+dojo.require('openils.acq.FundingSource');
+dojo.require('openils.Event');
+dojo.require('openils.User');
+dojo.require('openils.Util');
+
+var fund = null;
+
+function getSummaryInfo(rowIndex) {
+ return new String(fund.summary()[this.field]);
+}
+
+function createAllocation(fields) {
+ fields.fund = fundID;
+ if(isNaN(fields.percent)) fields.percent = null;
+ if(isNaN(fields.amount)) fields.amount = null;
+ //openils.acq.Fund.createAllocation(fields, resetPage);
+ openils.acq.Fund.createAllocation(fields,
+ function(r){location.href = location.href;});
+}
+
+function getOrgInfo(rowIndex) {
+ data = fundGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return fieldmapper.aou.findOrgUnit(data.org).shortname();
+}
+
+function getXferDest(rowIndex) {
+ data = this.grid.model.getRow(rowIndex);
+ if(!(data && data.xfer_destination)) return '';
+ return data.xfer_destination;
+}
+
+function loadFundGrid() {
+ var store = new dojo.data.ItemFileReadStore({data:acqf.toStoreData([fund])});
+ var model = new dojox.grid.data.DojoData(
+ null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ fundGrid.setModel(model);
+ fundGrid.update();
+}
+
+function loadAllocationGrid() {
+ if(fundAllocationGrid.isLoaded) return;
+ var store = new dojo.data.ItemFileReadStore({data:acqfa.toStoreData(fund.allocations())});
+ var model = new dojox.grid.data.DojoData(
+ null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ fundAllocationGrid.setModel(model);
+ fundAllocationGrid.update();
+ fundAllocationGrid.isLoaded = true;
+}
+
+function loadDebitGrid() {
+ if(fundDebitGrid.isLoaded) return;
+ var store = new dojo.data.ItemFileReadStore({data:acqfa.toStoreData(fund.debits())});
+ var model = new dojox.grid.data.DojoData(
+ null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ fundDebitGrid.setModel(model);
+ fundDebitGrid.update();
+ fundDebitGrid.isLoaded = true;
+}
+
+function fetchFund() {
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.fund.retrieve'],
+ { async: true,
+ params: [
+ openils.User.authtoken, fundID,
+ {flesh_summary:1, flesh_allocations:1, flesh_debits:1}
+ /* TODO grab allocations and debits only on as-needed basis */
+ ],
+ oncomplete: function(r) {
+ fund = r.recv().content();
+ loadFundGrid(fund);
+ }
+ }
+ );
+}
+
+openils.Util.addOnLoad(fetchFund);
--- /dev/null
+dojo.require("dijit.Dialog");
+dojo.require('dijit.layout.TabContainer');
+dojo.require('dijit.layout.ContentPane');
+dojo.require("dijit.form.FilteringSelect");
+dojo.require("dijit.form.Textarea");
+dojo.require("dijit.form.CurrencyTextBox");
+dojo.require('dojox.grid.Grid');
+
+dojo.require("fieldmapper.OrgUtils");
+dojo.require('openils.acq.FundingSource');
+dojo.require('openils.acq.Fund');
+dojo.require('openils.Event');
+dojo.require('openils.Util');
+
+var ses = new OpenSRF.ClientSession('open-ils.acq');
+var fundingSource = null;
+
+function resetPage() {
+ fundingSource = null;
+ fsCreditGrid.isLoaded = false;
+ fsAllocationGrid.isLoaded = false;
+ loadFS();
+}
+
+/** creates a new funding_source_credit from the dialog ----- */
+function applyFSCredit(fields) {
+ fields.funding_source = fundingSourceID;
+ openils.acq.FundingSource.createCredit(fields, resetPage);
+}
+
+function applyFSAllocation(fields) {
+ fields.funding_source = fundingSourceID;
+ if(isNaN(fields.percent)) fields.percent = null;
+ if(isNaN(fields.amount)) fields.amount = null;
+ openils.acq.Fund.createAllocation(fields, resetPage);
+}
+
+/** fetch the fleshed funding source ----- */
+function loadFS() {
+ var req = ses.request(
+ 'open-ils.acq.funding_source.retrieve',
+ openils.User.authtoken, fundingSourceID,
+ {flesh_summary:1, flesh_credits:1,flesh_allocations:1}
+ );
+
+ req.oncomplete = function(r) {
+ var msg = req.recv();
+ fundingSource = msg.content();
+ var evt = openils.Event.parse(fundingSource);
+ if(evt) {
+ alert(evt);
+ return;
+ }
+ loadFSGrid();
+ }
+ req.send();
+}
+
+/** Some grid rendering accessor functions ----- */
+function getOrgInfo(rowIndex) {
+ data = fundingSourceGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return fieldmapper.aou.findOrgUnit(data.owner).shortname();
+}
+
+function getSummaryInfo(rowIndex) {
+ return new String(fundingSource.summary()[this.field]);
+}
+
+/** builds the credits grid ----- */
+function loadFSGrid() {
+ if(!fundingSource) return;
+ var store = new dojo.data.ItemFileReadStore({data:acqfs.toStoreData([fundingSource])});
+ var model = new dojox.grid.data.DojoData(null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ fundingSourceGrid.setModel(model);
+ fundingSourceGrid.update();
+}
+
+
+/** builds the credits grid ----- */
+function loadCreditGrid() {
+ if(fsCreditGrid.isLoaded) return;
+ var store = new dojo.data.ItemFileReadStore({data:acqfa.toStoreData(fundingSource.credits())});
+ var model = new dojox.grid.data.DojoData(null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ fsCreditGrid.setModel(model);
+ fsCreditGrid.update();
+ fsCreditGrid.isLoaded = true;
+}
+
+/** builds the allocations grid ----- */
+function loadAllocationGrid() {
+ if(fsAllocationGrid.isLoaded) return;
+ var store = new dojo.data.ItemFileReadStore({data:acqfa.toStoreData(fundingSource.allocations())});
+ var model = new dojox.grid.data.DojoData(null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ fsAllocationGrid.setModel(model);
+ fsAllocationGrid.update();
+ fsAllocationGrid.isLoaded = true;
+}
+
+openils.Util.addOnLoad(loadFS);
--- /dev/null
+dojo.require("dijit.Dialog");
+dojo.require('dijit.layout.TabContainer');
+dojo.require('dijit.layout.ContentPane');
+dojo.require('dijit.form.FilteringSelect');
+dojo.require('dojox.grid.Grid');
+dojo.require("fieldmapper.OrgUtils");
+dojo.require('openils.acq.Provider');
+dojo.require('openils.Event');
+dojo.require('openils.User');
+dojo.require('openils.Util');
+
+var provider = null;
+var marcRegex = /^\/\/\*\[@tag="(\d+)"]\/\*\[@code="(\w)"]$/;
+
+function getOrgInfo(rowIndex) {
+ data = providerGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return fieldmapper.aou.findOrgUnit(data.owner).shortname();
+}
+
+function getTag(rowIdx) {
+ data = padGrid.model.getRow(rowIdx);
+ if(!data) return;
+ return data.xpath.replace(marcRegex, '$1');
+}
+
+function getSubfield(rowIdx) {
+ data = padGrid.model.getRow(rowIdx);
+ if(!data) return;
+ return data.xpath.replace(marcRegex, '$2');
+}
+
+
+function loadProviderGrid() {
+ var store = new dojo.data.ItemFileReadStore({data:acqpro.toStoreData([provider])});
+ var model = new dojox.grid.data.DojoData(
+ null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ providerGrid.setModel(model);
+ providerGrid.update();
+}
+
+function loadPADGrid() {
+ openils.acq.Provider.retrieveLineitemProviderAttrDefs(providerId,
+ function(attrs) {
+ var store = new dojo.data.ItemFileReadStore({data:acqlipad.toStoreData(attrs)});
+ var model = new dojox.grid.data.DojoData(
+ null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ padGrid.setModel(model);
+ padGrid.update();
+ }
+ );
+}
+
+
+function fetchProvider() {
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.provider.retrieve'],
+ { async: true,
+ params: [ openils.User.authtoken, providerId ],
+ oncomplete: function(r) {
+ provider = r.recv().content();
+ loadProviderGrid(provider);
+ }
+ }
+ );
+}
+
+function createOrderRecordField(fields) {
+ fields.provider = providerId;
+ if(!fields.xpath)
+ fields.xpath = '//*[@tag="'+fields.tag+'"]/*[@code="'+fields.subfield+'"]';
+ delete fields.tag;
+ delete fields.subfield;
+ openils.acq.Provider.createLineitemProviderAttrDef(fields,
+ function(id) {
+ loadPADGrid();
+ }
+ );
+}
+
+function setORDesc() {
+ var code = dijit.byId('oils-acq-provider-or-code');
+ var desc = dijit.byId('oils-acq-provider-or-desc');
+ desc.setValue(code.getDisplayedValue());
+}
+
+function deleteORDataFields() {
+ var list = []
+ var selected = padGrid.selection.getSelected();
+ for(var idx = 0; idx < selected.length; idx++)
+ list.push(padGrid.model.getRow(selected[idx]).id);
+ openils.acq.Provider.lineitemProviderAttrDefDeleteList(
+ list, function(){loadPADGrid();});
+}
+
+
+openils.Util.addOnLoad(fetchProvider);
+
+
--- /dev/null
+dojo.require('dojox.form.CheckedMultiSelect');
+dojo.require('fieldmapper.Fieldmapper');
+dojo.require('dijit.ProgressBar');
+dojo.require('dijit.form.Form');
+dojo.require('dijit.form.TextBox');
+dojo.require('dijit.form.NumberSpinner');
+dojo.require('openils.Event');
+dojo.require('openils.acq.Picklist');
+dojo.require('openils.acq.Lineitem');
+dojo.require('openils.User');
+dojo.require('openils.Util');
+
+var searchFields = [];
+var resultPicklist;
+var resultLIs;
+var selectedLIs;
+var recvCount = 0;
+var sourceCount = 0; // how many sources are we searching
+var user = new openils.User();
+var searchLimit = 10;
+
+function drawForm() {
+
+ var sources = fieldmapper.standardRequest(
+ ['open-ils.search', 'open-ils.search.z3950.retrieve_services'],
+ [user.authtoken]
+ );
+
+ openils.Event.parse_and_raise(sources);
+
+ for(var name in sources) {
+ source = sources[name];
+ bibSourceSelect.addOption(name, name+':'+source.host);
+ for(var attr in source.attrs)
+ if(!attr.match(/^#/)) // xml comment nodes
+ searchFields.push(source.attrs[attr]);
+ }
+
+ searchFields = searchFields.sort(
+ function(a,b) {
+ if(a.label < b.label)
+ return -1;
+ if(a.label > b.label)
+ return 1;
+ return 0;
+ }
+ );
+
+ var tbody = dojo.byId('oils-acq-search-fields-tbody');
+ var tmpl = tbody.removeChild(dojo.byId('oils-acq-search-fields-template'));
+
+ for(var f in searchFields) {
+ var field = searchFields[f];
+ if(dijit.byId('text_input_'+field.name)) continue;
+ var row = tmpl.cloneNode(true);
+ tbody.insertBefore(row, dojo.byId('oils-acq-seach-fields-count-row'));
+ var labelCell = dojo.query('[name=label]', row)[0];
+ var inputCell = dojo.query('[name=input]', row)[0];
+ labelCell.appendChild(document.createTextNode(field.label));
+ input = new dijit.form.TextBox({name:field.name, label:field.label, id:'text_input_'+field.name});
+ inputCell.appendChild(input.domNode);
+ }
+}
+
+function doSearch(values) {
+ dojo.style('searchProgress', 'visibility', 'visible');
+ searchProgress.update({progress: 0});
+
+ search = {
+ service : [],
+ username : [],
+ password : [],
+ search : {},
+ limit : values.limit,
+ offset : searchOffset
+ };
+ searchLimit = values.limit;
+ delete values.limit;
+
+ var selected = bibSourceSelect.getValue();
+ for(var i = 0; i < selected.length; i++) {
+ search.service.push(selected[i]);
+ search.username.push('');
+ search.password.push('');
+ sourceCount++;
+ }
+
+ for(var v in values) {
+ if(values[v]) {
+ var input = dijit.byId('text_input_'+v);
+ search.search[v] = values[v];
+ }
+ }
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.picklist.search.z3950'],
+ { async: true,
+ params: [user.authtoken, search],
+ onresponse: handleResult,
+ }
+ );
+}
+
+function handleResult(r) {
+ var result = r.recv().content();
+ if(openils.Event.parse(result)) {
+ alert(openils.Event.parse(result));
+ dojo.style('searchProgress', 'visibility', 'hidden');
+ return;
+ }
+ if(result.complete)
+ return viewResults(result.picklist_id);
+ searchProgress.update({maximum: result.total, progress: result.progress});
+}
+
+function viewResults(plId) {
+ var plist = new openils.acq.Picklist(plId,
+ function(model) {
+ resultLIs = plist._items;
+ dojo.style('oils-acq-pl-search-results', 'visibility', 'visible');
+ JUBGrid.populate(plResultGrid, model, plist._items);
+ },
+ {flesh_attrs:1, clear_marc:1, limit: searchLimit}
+ );
+ resultPicklist = plist._plist;
+}
+
+function loadPLSelect() {
+ var plList = [];
+ function handleResponse(r) {
+ plList.push(r.recv().content());
+ }
+ var method = 'open-ils.acq.picklist.user.retrieve';
+ fieldmapper.standardRequest(
+ ['open-ils.acq', method],
+ { async: true,
+ params: [openils.User.authtoken],
+ onresponse: handleResponse,
+ oncomplete: function() {
+ plAddExistingSelect.store =
+ new dojo.data.ItemFileReadStore({data:acqpl.toStoreData(plList)});
+ plAddExistingSelect.setValue();
+ }
+ }
+ );
+}
+
+
+function saveResults(values) {
+ selectedLIs = resultLIs;
+
+ if(values.which == 'selected') {
+ selectedLIs = [];
+ var selected = plResultGrid.selection.getSelected();
+ for(var idx = 0; idx < selected.length; idx++) {
+ var rowIdx = selected[idx];
+ var id = plResultGrid.model.getRow(rowIdx).id;
+ for(var i = 0; i < resultLIs.length; i++) {
+ var pl = resultLIs[i];
+ if(pl.id() == id) {
+ selectedLIs.push(pl);
+ }
+ }
+ }
+ }
+
+ if(values.new_name && values.new_name != '') {
+ // save selected lineitems to a new picklist
+ if(values.which = 'selected') {
+ openils.acq.Picklist.create(
+ {name: values.new_name},
+ function(id) {
+ updateLiList(id, selectedLIs, 0,
+ function(){location.href = 'view/' + id});
+ }
+ );
+ } else {
+ // save all == change the name of the results picklist
+ resultPicklist.name(values.new_name);
+ openils.acq.Picklist.update(resultPicklist,
+ function(stat) {
+ location.href = 'view/' + resultPicklist.id();
+ }
+ );
+ }
+ } else if(values.existing_pl) {
+ // update lineitems to use an existing picklist
+ updateLiList(values.existing_pl, selectedLIs, 0,
+ function(){location.href = 'view/' + values.existing_pl});
+ }
+}
+
+function updateLiList(pl, list, idx, oncomplete) {
+ if(idx >= list.length)
+ return oncomplete();
+ var li = selectedLIs[idx];
+ li.picklist(pl);
+ new openils.acq.Lineitem({lineitem:li}).update(
+ function(r) {
+ updateLiList(pl, list, ++idx, oncomplete);
+ }
+ );
+}
+
+openils.Util.addOnLoad(drawForm);
--- /dev/null
+dojo.require('dojox.grid.Grid');
+dojo.require('dijit.Dialog');
+dojo.require('dijit.form.Button');
+dojo.require('dijit.form.TextBox');
+dojo.require('dijit.form.Button');
+dojo.require('openils.acq.Picklist');
+dojo.require('openils.Util');
+
+var plList = [];
+var listAll = false;
+
+function makeGridFromList() {
+ var store = new dojo.data.ItemFileReadStore({data:acqpl.toStoreData(plList)});
+ var model = new dojox.grid.data.DojoData(null, store,
+ {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ plListGrid.setModel(model);
+ plListGrid.update();
+}
+
+
+function loadGrid() {
+ var method = 'open-ils.acq.picklist.user.retrieve.atomic';
+ if(listAll)
+ method = method.replace(/user/, 'user.all');
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', method],
+ { async: true,
+ params: [openils.User.authtoken,
+ {flesh_lineitem_count:1, flesh_username:1}],
+ oncomplete: function(r) {
+ var resp = r.recv().content();
+ if(e = openils.Event.parse(resp))
+ return alert(e);
+ plList = resp;
+ makeGridFromList();
+ }
+ }
+ );
+}
+
+function createPL(fields) {
+ if(fields.name == '') return;
+ openils.acq.Picklist.create(fields,
+ function(plId) {
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.picklist.retrieve'],
+ { async: true,
+ params: [openils.User.authtoken, plId,
+ {flesh_lineitem_count:1, flesh_username:1}],
+ oncomplete: function(r) {
+ var pl = r.recv().content();
+ plList.push(pl);
+ makeGridFromList();
+ }
+ }
+ );
+ }
+ );
+}
+
+function deleteFromGrid() {
+ var list = []
+ var selected = plListGrid.selection.getSelected();
+ for(var idx = 0; idx < selected.length; idx++) {
+ var rowIdx = selected[idx];
+ var id = plListGrid.model.getRow(rowIdx).id;
+ for(var i = 0; i < plList.length; i++) {
+ var pl = plList[i];
+ if(pl.id() == id && pl.owner() == new openils.User().user.usrname()) {
+ list.push(id);
+ plList = (plList.slice(0, i) || []).concat(plList.slice(i+1, plList.length) || []);
+ }
+ }
+ }
+ openils.acq.Picklist.deleteList(list, function() { makeGridFromList(); });
+}
+
+openils.Util.addOnLoad(loadGrid);
+
+
--- /dev/null
+dojo.require('fieldmapper.Fieldmapper');
+dojo.require('dijit.ProgressBar');
+dojo.require('dijit.form.Form');
+dojo.require('dijit.form.TextBox');
+dojo.require('dijit.form.CheckBox');
+dojo.require('dijit.form.FilteringSelect');
+dojo.require('dijit.form.Button');
+dojo.require("dijit.Dialog");
+dojo.require('openils.Event');
+dojo.require('openils.Util');
+dojo.require('openils.acq.Lineitem');
+dojo.require('openils.acq.Provider');
+dojo.require('openils.acq.PO');
+dojo.require('openils.widget.OrgUnitFilteringSelect');
+
+var recvCount = 0;
+var createAssetsSelected = false;
+var createDebitsSelected = false;
+
+var lineitems = [];
+
+function drawForm() {
+ buildProviderSelect(providerSelector);
+}
+
+function buildProviderSelect(sel, oncomplete) {
+ openils.acq.Provider.createStore(
+ function(store) {
+ sel.store = new dojo.data.ItemFileReadStore({data:store});
+ if(oncomplete)
+ oncomplete();
+ },
+ 'MANAGE_PROVIDER'
+ );
+}
+
+var liReceived;
+function doSearch(values) {
+ var search = {};
+ for(var v in values) {
+ var val = values[v];
+ if(val != null && val != '')
+ search[v] = val;
+ }
+
+ if(values.state == 'approved')
+ dojo.style('oils-acq-li-search-po-create', 'visibility', 'visible');
+ else
+ dojo.style('oils-acq-li-search-po-create', 'visibility', 'hidden');
+
+ //search = [search, {limit:searchLimit, offset:searchOffset}];
+ search = [search, {}];
+ options = {clear_marc:1, flesh_attrs:1};
+
+ liReceived = 0;
+ lineitems = [];
+ dojo.style('searchProgress', 'visibility', 'visible');
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem.search'],
+ { async: true,
+ params: [openils.User.authtoken, search, options],
+ onresponse: handleResult,
+ oncomplete: viewList
+ }
+ );
+}
+
+function handleResult(r) {
+ var result = r.recv().content();
+ searchProgress.update({maximum: searchLimit, progress: ++liReceived});
+ lineitems.push(result);
+}
+
+function viewList() {
+ dojo.style('searchProgress', 'visibility', 'hidden');
+ dojo.style('oils-acq-li-search-result-grid', 'visibility', 'visible');
+ var store = new dojo.data.ItemFileWriteStore(
+ {data:jub.toStoreData(lineitems, null,
+ {virtualFields:['estimated_price', 'actual_price']})});
+ var model = new dojox.grid.data.DojoData(
+ null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ JUBGrid.populate(liGrid, model, lineitems);
+}
+
+function createPOFromLineitems(fields) {
+ var po = new acqpo();
+ po.provider(newPOProviderSelector.getValue());
+ createAssetsSelected = fields.create_assets;
+ createDebitsSelected = fields.create_debits;
+
+ if(fields.which == 'selected') {
+ // find the selected lineitems
+ var selected = liGrid.selection.getSelected();
+ var selList = [];
+ for(var idx = 0; idx < selected.length; idx++) {
+ var rowIdx = selected[idx];
+ var id = liGrid.model.getRow(rowIdx).id;
+ for(var i = 0; i < lineitems.length; i++) {
+ var li = lineitems[i];
+ if(li.id() == id && !li.purchase_order() && li.state() == 'approved')
+ selList.push(lineitems[i]);
+ }
+ }
+ } else {
+ selList = lineitems;
+ }
+
+ if(selList.length == 0) return;
+
+ openils.acq.PO.create(po,
+ function(poId) {
+ if(e = openils.Event.parse(poId))
+ return alert(e);
+ updateLiList(poId, selList);
+ }
+ );
+}
+
+function updateLiList(poId, selList) {
+ _updateLiList(poId, selList, 0);
+}
+
+function checkCreateDebits(poId) {
+ if(!createDebitsSelected)
+ return viewPO(poId);
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.purchase_order.debits.create'],
+ { async: true,
+ params: [openils.User.authtoken, poId, {encumbrance:1}],
+ oncomplete : function(r) {
+ var total = r.recv().content();
+ if(e = openils.Event.parse(total))
+ return alert(e);
+ viewPO(poId);
+ }
+ }
+ );
+}
+
+function viewPO(poId) {
+ location.href = 'view/' + poId;
+}
+
+function _updateLiList(poId, selList, idx) {
+ if(idx >= selList.length) {
+ if(createAssetsSelected)
+ return createAssets(poId);
+ else
+ return checkCreateDebits(poId);
+ }
+ var li = selList[idx];
+ li.purchase_order(poId);
+ li.state('in-process');
+ new openils.acq.Lineitem({lineitem:li}).update(
+ function(stat) {
+ _updateLiList(poId, selList, ++idx);
+ }
+ );
+}
+
+function createAssets(poId) {
+ searchProgress.update({progress: 0});
+ dojo.style('searchProgress', 'visibility', 'visible');
+
+ function onresponse(r) {
+ var stat = r.recv().content();
+ if(e = openils.Event.parse(stat))
+ return alert(e);
+ searchProgress.update({maximum: stat.total, progress: stat.progress});
+ }
+
+ function oncomplete(r) {
+ dojo.style('searchProgress', 'visibility', 'hidden');
+ checkCreateDebits(poId);
+ }
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq','open-ils.acq.purchase_order.assets.create'],
+ { async: true,
+ params: [openils.User.authtoken, poId],
+ onresponse : onresponse,
+ oncomplete : oncomplete
+ }
+ );
+}
+
+
+openils.Util.addOnLoad(drawForm);
+
--- /dev/null
+dojo.require('dijit.form.Form');
+dojo.require('dijit.form.Button');
+dojo.require('dijit.form.FilteringSelect');
+dojo.require('dijit.form.NumberTextBox');
+dojo.require('dojox.grid.Grid');
+dojo.require('openils.acq.Provider');
+dojo.require('fieldmapper.OrgUtils');
+dojo.require('dojo.date.locale');
+dojo.require('dojo.date.stamp');
+dojo.require('openils.User');
+dojo.require('openils.Util');
+dojo.require('openils.widget.OrgUnitFilteringSelect');
+
+function getOrgInfo(rowIndex) {
+ data = poGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return fieldmapper.aou.findOrgUnit(data.owner).shortname();
+}
+
+function getProvider(rowIndex) {
+ data = poGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return openils.acq.Provider.retrieve(data.provider).name();
+}
+
+function getPOOwner(rowIndex) {
+ data = poGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return new openils.User({id:data.owner}).user.usrname();
+}
+
+function getDateTimeField(rowIndex) {
+ data = poGrid.model.getRow(rowIndex);
+ if(!data) return;
+ var date = dojo.date.stamp.fromISOString(data[this.field]);
+ return dojo.date.locale.format(date, {formatLength:'medium'});
+}
+
+function doSearch(fields) {
+ var itemList = [];
+ if(!isNaN(fields.id))
+ fields = {id:fields.id};
+ else
+ delete fields.id;
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.purchase_order.search'],
+ {
+ async:1,
+ params: [openils.User.authtoken, fields],
+ onresponse : function(r) {
+ var msg = r.recv();
+ if(msg) itemList.push(msg.content());
+ },
+ oncomplete : function(r) {
+ dojo.style('po-grid', 'visibility', 'visible');
+ var store = new dojo.data.ItemFileReadStore({data:acqpo.toStoreData(itemList)});
+ var model = new dojox.grid.data.DojoData(null, store,
+ {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ poGrid.setModel(model);
+ poGrid.update();
+ },
+ }
+ );
+}
+
+function loadForm() {
+
+ /* load the providers */
+ openils.acq.Provider.createStore(
+ function(store) {
+ providerSelector.store =
+ new dojo.data.ItemFileReadStore({data:store});
+ },
+ 'MANAGE_PROVIDER'
+ );
+
+ new openils.User().buildPermOrgSelector('VIEW_PURCHASE_ORDER', poSearchOrderingAgencySelect);
+}
+
+openils.Util.addOnLoad(loadForm);
--- /dev/null
+dojo.require("dijit.Dialog");
+dojo.require('dijit.form.FilteringSelect');
+dojo.require('dijit.layout.TabContainer');
+dojo.require('dijit.layout.ContentPane');
+dojo.require('dojox.grid.Grid');
+dojo.require('openils.acq.PO');
+dojo.require('openils.Event');
+dojo.require('openils.User');
+dojo.require('openils.Util');
+dojo.require('fieldmapper.OrgUtils');
+dojo.require('openils.acq.Provider');
+dojo.require('openils.acq.Lineitem');
+dojo.require('dojo.date.locale');
+dojo.require('dojo.date.stamp');
+
+var PO = null;
+var lineitems = [];
+
+function getOrgInfo(rowIndex) {
+ data = poGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return fieldmapper.aou.findOrgUnit(data.owner).shortname();
+}
+
+function getProvider(rowIndex) {
+ data = poGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return openils.acq.Provider.retrieve(data.provider).code();
+}
+
+function getPOOwner(rowIndex) {
+ data = poGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return new openils.User({id:data.owner}).user.usrname();
+}
+
+function getDateTimeField(rowIndex) {
+ data = poGrid.model.getRow(rowIndex);
+ if(!data) return;
+ var date = dojo.date.stamp.fromISOString(data[this.field]);
+ return dojo.date.locale.format(date, {formatLength:'medium'});
+}
+
+function loadPOGrid() {
+ if(!PO) return;
+ var store = new dojo.data.ItemFileReadStore({data:acqpo.toStoreData([PO])});
+ var model = new dojox.grid.data.DojoData(
+ null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ poGrid.setModel(model);
+ poGrid.update();
+}
+
+function loadLIGrid() {
+ if(liGrid.isLoaded) return;
+
+ function load(po) {
+ lineitems = po.lineitems();
+ var store = new dojo.data.ItemFileReadStore({data:jub.toStoreData(lineitems)});
+ var model = new dojox.grid.data.DojoData(
+ null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ JUBGrid.populate(liGrid, model, lineitems)
+ }
+
+ openils.acq.PO.retrieve(poId, load, {flesh_lineitems:1, clear_marc:1});
+ liGrid.isLoaded = true;
+}
+
+function loadPage() {
+ fetchPO();
+}
+
+function fetchPO() {
+ openils.acq.PO.retrieve(poId,
+ function(po) {
+ PO = po;
+ loadPOGrid();
+ },
+ {flesh_lineitem_count:1}
+ );
+}
+
+openils.Util.addOnLoad(loadPage);
--- /dev/null
+dojo.require('fieldmapper.Fieldmapper');
+dojo.require('dijit.ProgressBar');
+dojo.require('dijit.form.Form');
+dojo.require('dijit.form.TextBox');
+dojo.require('dijit.form.CheckBox');
+dojo.require('dijit.form.FilteringSelect');
+dojo.require('dijit.form.Button');
+dojo.require("dijit.Dialog");
+dojo.require('openils.Event');
+dojo.require('openils.Util');
+dojo.require('openils.acq.Lineitem');
+dojo.require('openils.widget.OrgUnitFilteringSelect');
+
+var lineitems = [];
+
+function drawForm() {
+ new openils.User().buildPermOrgSelector('VIEW_PURCHASE_ORDER', orderingAgencySelect);
+}
+
+var liReceived;
+function doSearch(values) {
+
+ var search = {
+ attr_values : [values.identifier],
+ po_agencies : (values.ordering_agency) ? [values.ordering_agency] : null,
+ li_states : ['in-process']
+ };
+
+ options = {clear_marc:1, flesh_attrs:1};
+ liReceived = 0;
+ dojo.style('searchProgress', 'visibility', 'visible');
+
+ fieldmapper.standardRequest(
+ ['open-ils.acq', 'open-ils.acq.lineitem.search.ident'],
+ { async: true,
+ params: [openils.User.authtoken, search, options],
+ onresponse: handleResult,
+ oncomplete: viewList
+ }
+ );
+}
+
+var searchLimit = 10; // ?
+function handleResult(r) {
+ var result = r.recv().content();
+ searchProgress.update({maximum: searchLimit, progress: ++liReceived});
+ lineitems.push(result);
+}
+
+function viewList() {
+ dojo.style('searchProgress', 'visibility', 'hidden');
+ dojo.style('oils-acq-recv-grid', 'visibility', 'visible');
+ var store = new dojo.data.ItemFileWriteStore(
+ {data:jub.toStoreData(lineitems, null,
+ {virtualFields:['estimated_price', 'actual_price']})});
+ var model = new dojox.grid.data.DojoData(
+ null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ JUBGrid.populate(liGrid, model, lineitems);
+}
+
+openils.Util.addOnLoad(drawForm);
+
--- /dev/null
+dojo.require("dijit.Dialog");
+dojo.require('dijit.layout.TabContainer');
+dojo.require('dijit.layout.ContentPane');
+dojo.require('dijit.form.FilteringSelect');
+dojo.require('dijit.form.TextBox');
+dojo.require('dojox.grid.Grid');
+dojo.require("fieldmapper.OrgUtils");
+dojo.require('openils.Event');
+dojo.require('openils.User');
+dojo.require('openils.acq.LineitemAttr');
+
+var provider = null;
+var marcRegex = /^\/\/\*\[@tag="(\d+)"]\/\*\[@code="(\w)"]$/;
+var attrDefStores;
+
+
+function getOrgInfo(rowIndex) {
+ data = providerGrid.model.getRow(rowIndex);
+ if(!data) return;
+ return fieldmapper.aou.findOrgUnit(data.owner).shortname();
+}
+
+function getTag(rowIdx) {
+ data = this.grid.model.getRow(rowIdx);
+ if(!data) return;
+ return data.xpath.replace(marcRegex, '$1');
+}
+
+function getSubfield(rowIdx) {
+ data = this.grid.model.getRow(rowIdx);
+ if(!data) return;
+ return data.xpath.replace(marcRegex, '$2');
+}
+
+
+function loadStores(onload) {
+ if(attrDefStores)
+ return onload();
+ openils.acq.LineitemAttr.createAttrDefStores(
+ function(stores) {
+ attrDefStores = stores;
+ onload();
+ }
+ )
+}
+
+
+function loadMarcAttrGrid() {
+ loadStores(
+ function() {
+ var store = new dojo.data.ItemFileReadStore({data:attrDefStores.marc});
+ var model = new dojox.grid.data.DojoData(
+ null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ liMarcAttrGrid.setModel(model);
+ liMarcAttrGrid.update();
+ }
+ );
+}
+
+function loadGeneratedAttrGrid() {
+ loadStores(
+ function() {
+ var store = new dojo.data.ItemFileReadStore({data:attrDefStores.generated});
+ var model = new dojox.grid.data.DojoData(
+ null, store, {rowsPerPage: 20, clientSort: true, query:{id:'*'}});
+ liGeneratedAttrGrid.setModel(model);
+ liGeneratedAttrGrid.update();
+ }
+ );
+}
+
+/*
+function createOrderRecordField(fields) {
+ fields.provider = providerId;
+ if(!fields.xpath)
+ fields.xpath = '//*[@tag="'+fields.tag+'"]/*[@code="'+fields.subfield+'"]';
+ delete fields.tag;
+ delete fields.subfield;
+ openils.acq.Provider.createLineitemProviderAttrDef(fields,
+ function(id) {
+ loadPADGrid();
+ }
+ );
+}
+
+function setORDesc() {
+ var code = dijit.byId('oils-acq-provider-or-code');
+ var desc = dijit.byId('oils-acq-provider-or-desc');
+ desc.setValue(code.getDisplayedValue());
+}
+
+function deleteORDataFields() {
+ var list = []
+ var selected = padGrid.selection.getSelected();
+ for(var idx = 0; idx < selected.length; idx++)
+ list.push(padGrid.model.getRow(selected[idx]).id);
+ openils.acq.Provider.lineitemProviderAttrDefDeleteList(
+ list, function(){loadPADGrid();});
+}
+*/
+
+
+//dojo.addOnLoad(loadLIAttrGrid);
+
+
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns='http://www.w3.org/1999/xhtml' lang='${locale}' xml:lang='${locale}'>
+ <head>
+ <link rel='stylesheet' type='text/css'
+ href='[% ctx.media_prefix %]/css/skin/[% ctx.skin %].css'></link>
+ <link rel='stylesheet' type='text/css'
+ href='[% ctx.media_prefix %]/css/theme/[% ctx.skin %].css'></link>
+ <script type="text/javascript" src="[% ctx.media_prefix %]/js/dojo/dojo/dojo.js"
+ djConfig="parseOnLoad: true, isDebug:true"></script>
+ <script type="text/javascript" src="[% ctx.media_prefix %]/js/dojo/opensrf/md5.js"></script>
+ <script>
+ var oilsCookieBase = '[% ctx.base_uri %]';
+ </script>
+ <script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/base.js"></script>
+ </head>
+ <body class='tundra'>
+
+ <!-- general purpose login dialog -->
+ <div style='display:none;' dojoType="dijit.Dialog" jsId='oilsLoginDialog' class='oils-login-dialog'>
+ <b>Please Login</b>
+ <form onsubmit='oilsDoLogin();'>
+ <table>
+ <tr>
+ <td>Username</td>
+ <td><input type='dijit.form.TextBox' id='oils-login-username'/></td>
+ </tr>
+ <tr>
+ <td>Password</td>
+ <td><input type='dijit.form.TextBox' id='oils-login-password' type='password'/></td>
+ </tr>
+ <tr>
+ <td colspan='2'>
+ <button type='submit' dojoType='dijit.form.Button'>Login</button>
+ </td>
+ </tr>
+ </table>
+ </form>
+ </div>
+ [% content %]
+ </body>
+</html>
--- /dev/null
+[%#-
+This template creates a split screen Dojo layout. The top frame
+of the screen holds a list of of JUBs, or titles. Clicking on a
+title in the top frame will load the purchase details for all the
+copies on order for that title into the bottom frame.
+
+To create a display for a set of JUBs, create a Dojo store and
+model for the set of JUBs, then place the following lines in your
+HTML where you want the display to appear:
+
+ <%namespace file='/oils/default/common/jubgrid.html' name='jubgrid'/>
+ ${jubgrid.jubgrid('dom_prefix', 'grid_jsid')}
+
+where 'dom_prefix' is a string that will be used as the prefix
+for the DOM notes that are created by this template, and
+'grid_jsid' is a valid JavaScript identifier that will name the
+DOM node to which the list of JUBs will be attached. For example
+
+ ${jubgrid.jubgrid('oils-acq-picklist', 'pickListGrid', hideDetails)}
+
+will create a Dojo grid with the DOM id of
+
+ 'oils-acq-picklist-JUB-grid'
+
+and a jsid of
+
+ pickListGrid
+
+To fill the grid with data, call the javascript function
+
+ JUBGrid.populate(grid_jsid, model)
+
+'grid_jsid' is the same javascript id that was used to
+instantiate the template, and model is a javascript variable
+pointing to the JUB model (and store) that you have created.
+-#%]
+
+[% UNLESS hide_details %]
+<div dojoType='dijit.layout.ContentPane' style='height:100%;'>
+[% END %]
+
+ <style type='text/css'>
+ .grid_container {width: 100%; height: 100%;}
+ </style>
+
+ <script src='[% ctx.media_prefix %]/js/ui/default/acq/common/jubgrid.js'> </script>
+ <script src='[% ctx.media_prefix %]/js/dojo/openils/CopyLocation.js'> </script>
+ <script type="text/javascript">
+ JUBGrid.getPO = function(rowIndex) {
+ var data = JUBGrid.jubGrid.model.getRow(rowIndex);
+ if (!(data && data.purchase_order)) return '';
+ return "<a href='[% ctx.base_uri %]/acq/po/view/" + data.purchase_order+"'>"+data.purchase_order+"</a>";
+ }
+ JUBGrid.jubGridLayout = [{
+ //noscroll: true,
+ cells: [[
+ {name: 'ID', field: 'id', width:'auto'},
+ {name: 'Title', width: "180px", get:JUBGrid.getJUBTitle},
+ {name: 'Author', get:JUBGrid.getJUBAuthor, width:'auto'},
+ {name: 'ISBN', get:JUBGrid.getJUBIsbn, width:'auto'},
+ {name: 'Pubdate', get:JUBGrid.getJUBPubdate, width:'auto'},
+ {name: 'Actual Price',
+ field:'actual_price',
+ get:JUBGrid.getJUBActualPrice,
+ editor:dojox.grid.editors.Dijit, width:'auto',
+ editorClass: "dijit.form.CurrencyTextBox"
+ },
+ {name: 'Estimated Price',
+ field:'estimated_price',
+ get:JUBGrid.getJUBEstimatedPrice, width:'auto',
+ editor:dojox.grid.editors.Dijit,
+ editorClass: "dijit.form.CurrencyTextBox"
+ },
+ {name: 'Vendor', width:'auto',
+ field: 'provider', get:JUBGrid.getProvider,
+ editor:openils.editors.ProviderSelectEditor,
+ },
+ {name: 'No. Copies', field: 'item_count', width:'auto'},
+ {name: 'State', field: 'state', width:'auto'},
+ {name: 'PO', get:JUBGrid.getPO, width:'auto'}
+ ]]
+ }];
+
+ JUBGrid.jubDetailGridLayout = [{
+ cells: [[
+ {name:"ID", field:"id"},
+ {name:"Fund", field:"fund",
+ get:JUBGrid.getLIDFundCode,
+ editor: openils.editors.FundSelectEditor,
+ },
+ {name:"Branch", field:"owning_lib",
+ get:JUBGrid.getLIDLibName,
+ editor: openils.editors.OrgUnitSelectEditor
+ },
+ {name:"Barcode", field:"barcode", width:'auto',
+ editor:dojox.grid.editors.Dijit,
+ editorClass: "dijit.form.TextBox"
+ },
+ {name:"Call Number", field:"cn_label", width:'auto',
+ editor:dojox.grid.editors.Dijit,
+ editorClass: "dijit.form.TextBox"
+ },
+ {name:"Shelving Location", field:"location", width:'auto',
+ editor:openils.editors.CopyLocationSelectEditor,
+ get:JUBGrid.getCopyLocation
+ },
+ {name:"Receive Time", width:'auto',
+ get:JUBGrid.getRecvTime
+ },
+ ]]
+ }];
+
+ JUBGrid.jubDetailGridLayoutReadOnly = [{
+ cells: [[
+ {name:'ID', field:"id"},
+ {name:'Fund', field:"fund",
+ get:JUBGrid.getLIDFundCode,
+ },
+ {name:'Branch', field:"owning_lib",
+ get:JUBGrid.getLIDLibName,
+ },
+ {name:'Barcode', field:"barcode", width:'auto'},
+ {name:'Call Number', field:"cn_label", width:'auto'},
+ {name:'Shelving Location', field:"location",
+ width:'auto', get:JUBGrid.getCopyLocation},
+ ]]
+ &