Feature #643

add price calculation for diffusion

Added by Mérour Xavier over 5 years ago. Updated about 4 years ago.

Status:ClosedStart date:03/14/2014
Priority:NormalDue date:
Assignee:-% Done:

0%

Category:SHOP
Target version:4.3.0
Sponsor: Ergonomic impact:
Functional impact:

Description

the need is to be able to calculate prices directly "on the fly" in the basket, before validating an order.

Many impacts :

in back-end

  • add a new role "pricing manager" in the "user" screen, tab "role" (the user can have the role in 0 to n organisms); "pricing manager" is an effective role related to organism and not to resource like "diffusion manager", "metadata editor", ...
  • in the SHOP webservice, the tarification and price calculation details must be delivered (see gliffy attachement number 4)
  • in the SHOP, add a new menu "pricing", the admin can configure the tax amount per categorie (see gliffy attachement 6); after discussion some implementation changes are validated:
    • cost per category is filled in category's edition form
    • other global parameter are integrated in SHOP options form ("Pricing" tab)
    • "Pricing" tab will have a message like "Category's cost are editable in category edition form (link to categories list)
  • in the SHOP, add a general parameter to enable/disable the online price calculation : if set to disable, the basket remain as in easySDI V4.1 and the "pricing manager" role is not available in CONTACT

in front-end

  • a new screen where the pricing manager will be able to define its entire price politic
    if the user can manage pricing for different organisms, the user must first choose for which organism he wants see the pricing.
    • first screen lists all organisms that a pricing manager is responsible for (with per organism filter); if the user is responsible of only one organism than the first screen displays automatically pricing's edition form

(see gliffy screen in attachement, number 2)

  • in "diffusion management" under "ressources". The attribute "tarif" will be move under "extraction" and will contain the following items :
    1. free
    2. fee without a pricing profile (if set, "Ask for estimate" button is available inside the basket beside "Place order" button)
    3. fee with a pricing profil
    - profil 1
    - profil 2
    - profil ...
    - ...
  • basket will evolve with all pricing information, per data supplier (see layout in attachement basket5.zip : html example)
    • Perimeter selection is pushed on top of basket (because perimeter is needed for price calculation and therefore should be defined by the user at first - not mandatory but in logical order)
    • Basket and display logic is described in attachment : new_basket_explain.png
  • price calculation MUST be done server-side for security reasons

Open questions :

- how to store pricing parameters for each elements n the basket ?
- which XML structure to add in the SHOP webservce to transmit prices details to providers ?

2_gliffy_gestion_tarification.png - gliffy screen number 2 (62.9 KB) Mérour Xavier, 04/24/2014 03:27 PM

4_gliffy_webservice.png - gliffy screen number 4 (17.3 KB) Mérour Xavier, 04/24/2014 03:27 PM

6_gliffy_tarification_backend.png (12.9 KB) Blatti Yves, 06/25/2014 04:17 PM

3_gliffy_formula.png (15.4 KB) Blatti Yves, 06/25/2014 04:19 PM

basket5.zip - Simplified verson of basket (1.29 MB) Blatti Yves, 06/25/2014 04:20 PM

new_basket_explain.png (131 KB) Blatti Yves, 06/26/2014 10:06 AM

sdi-getOrders.xsd Magnifier (17.8 KB) Villemagne Jérôme, 08/27/2014 12:40 PM

sdi-getOrders.xsd Magnifier (17.8 KB) Villemagne Jérôme, 08/27/2014 12:40 PM

basket7.zip - Updated version of basket, with ajax post and status check sample, needs to be run with php (1.29 MB) Blatti Yves, 09/03/2014 01:38 PM

setproduct-sample.xml Magnifier (5.76 KB) Villemagne Jérôme, 09/03/2014 02:23 PM

setproduct.xsd Magnifier (13 KB) Villemagne Jérôme, 09/03/2014 02:23 PM

setproduct-sample.xml Magnifier (4.98 KB) Villemagne Jérôme, 09/04/2014 08:24 AM

setproduct.xsd Magnifier (7.81 KB) Villemagne Jérôme, 09/04/2014 08:24 AM

History

#1 Updated by Mérour Xavier over 5 years ago

  • Assignee set to Mérour Xavier

gliffy has to be changed (basket)

#2 Updated by Mérour Xavier over 5 years ago

question open : tax rate (TVA) ???

#3 Updated by Mérour Xavier over 5 years ago

  • Assignee changed from Mérour Xavier to Blatti Yves

Work on basket to do (layout proposal with Gliffy)

Thanks Yves.

#4 Updated by Mérour Xavier over 5 years ago

  • Description updated (diff)
  • Assignee changed from Blatti Yves to Mérour Xavier

#5 Updated by Mérour Xavier over 5 years ago

#6 Updated by Mérour Xavier over 5 years ago

  • Assignee changed from Mérour Xavier to Magoni Bruno

Hi Bruno,

We completed this new feature description. Hope you have now all you need to digg in !
Please do not hesitate to write your questions here.

Then we will need a vote :-)

#7 Updated by Mérour Xavier over 5 years ago

Hi,

We realized we should complete the description of this feature in three areas :

- price calculation details (rules of rounding mainly)
- pice details given to the third-party via WPS
- XML structure in WPS

We get back to you !

#8 Updated by Magoni Bruno over 5 years ago

  • Assignee changed from Magoni Bruno to Mérour Xavier

#9 Updated by Blatti Yves about 5 years ago

  • File deleted (6_gliffy_tarification_backend.png)

#11 Updated by Blatti Yves about 5 years ago

  • File deleted (3_gliffy_formula.png)

#13 Updated by Blatti Yves about 5 years ago

  • File deleted (basket4.zip)

#14 Updated by Blatti Yves about 5 years ago

#15 Updated by Blatti Yves about 5 years ago

  • File new_basket_explain.png added

#16 Updated by Blatti Yves about 5 years ago

  • File deleted (new_basket_explain.png)

#17 Updated by Blatti Yves about 5 years ago

  • File new_basket_explain.png added

#18 Updated by Blatti Yves about 5 years ago

Pseudo-code for price calculation (with pricing profile) of an item in basket:

//$diffusion represents the "product" (jos_sdi_diffusion) and linked properties like princing profile
//$order represents the order, with the client, third party the surface...
//$config is shop configuration
//$provider is the organism that provides the product (diffusion)
//$provider.princing is the configuration of pricing at organism level

$priceExclTaxes = 0;
$finalPriceExclTaxes = 0;
$finalPriceInclTaxes = 0;

$floorReached = false;
$ceilingReached = false;
$isFreeForMyCategory = false;
$freeForMyCategoryName = "";
$hasRebateForCategory = false;
$rebateForMyCategory = 0;
$rebateForMyCategoryName = "";
$isFreeInternalOrder = false;

$provider = $diffusion.getOrganism();

// if a third party is set, use it for pricing instead of client
if($order.thirdparty != null){
    $organismForPricing  = $order.thirdparty.organism;
}else{
    $organismForPricing  = $order.client.organism;
}

// if "free for internal orders" is set -> no pricing
if( ! ($organismForPricing == $provider) && $provider.princing.doNotBillInternalOrders ){

    // Test if free product by category
    foreach($organismForPricing.categories[] as $categ){
        if($diffusion.princingProfile.isFreeForCateg($categ)){
            // Yeah product is free for this category !
            $isFreeForMyCategory = true;
            $freeForMyCategoryName = $categ.name;
            break;
        }
    }

    // if product is not free for any of the (cleint/thridparty) organism category
    if(!$isFreeForMyCategory){
        $priceExclTaxes = $diffusion.princingProfile.fixedBasePrice
                        + $diffusion.princingProfile.pricePerSurface * $order.surface;
        //Adapt with ceil and floor
        if ($priceExclTaxes > $diffusion.princingProfile.ceilingPrice){
            $ceilingReached = true;
            $priceExclTaxes = $diffusion.princingProfile.ceilingPrice;
        }
        if ($priceExclTaxes < $diffusion.princingProfile.floorPrice){
            $floorReached = true;
            $priceExclTaxes = $diffusion.princingProfile.floorPrice;
        }

        //Check org. category has a rebate given by provider organism
        foreach($organismForPricing.categories[] as $categ){
            $reb = $provider.princing.getRebateForCateg($categ);
            if($reb > $rebateForMyCategory){
                $rebateForMyCategory = $reb;
                $hasRebateForCategory = true;
                $rebateForMyCategoryName = $categ.name;
            }
        }

        //Apply rebate
        if($hasRebateForCategory){
            $finalPriceExclTaxes = $priceExclTaxes - $priceExclTaxes * ($rebateForMyCategory / 100)
        }else{
            $finalPriceExclTaxes = $priceExclTaxes;
        }

        //Round excl. tax price to cent
        $finalPriceExclTaxes = round($finalPriceExclTaxes,2);

        //Apply VAT     
        $finalPriceInclTaxes = $finalPriceExclTaxes + ($finalPriceExclTaxes * ($config.VAT / 100));
    }
}

#19 Updated by Blatti Yves about 5 years ago

  • File deleted (new_basket_explain.png)

#21 Updated by Blatti Yves about 5 years ago

  • Description updated (diff)

#22 Updated by Mérour Xavier about 5 years ago

  • Assignee changed from Mérour Xavier to Magoni Bruno

#23 Updated by Magoni Bruno about 5 years ago

  • Status changed from New to Request For Votes
  • % Done changed from 10 to 0

Thanks for voting on such feature described by Xavier...

#24 Updated by Magoni Bruno about 5 years ago

+1

#25 Updated by Mérour Xavier about 5 years ago

Surprisingly... it's a +1 for me ;-)

#26 Updated by Teixeira Jérôme about 5 years ago

Olà amigos,
+1

#27 Updated by Magoni Bruno about 5 years ago

  • Status changed from Request For Votes to Accepted

#28 Updated by Magoni Bruno about 5 years ago

  • Description updated (diff)

#29 Updated by Magoni Bruno about 5 years ago

  • Description updated (diff)

#30 Updated by Magoni Bruno about 5 years ago

  • Description updated (diff)

#31 Updated by Magoni Bruno about 5 years ago

  • Assignee changed from Magoni Bruno to Villemagne Jérôme

#32 Updated by Magoni Bruno about 5 years ago

  • Description updated (diff)

#33 Updated by Magoni Bruno about 5 years ago

  • Description updated (diff)

#34 Updated by Magoni Bruno about 5 years ago

  • Status changed from Accepted to Affected

#35 Updated by Magoni Bruno about 5 years ago

  • Target version set to Unplanned

#36 Updated by Magoni Bruno about 5 years ago

  • Target version changed from Unplanned to 158

#37 Updated by Blatti Yves about 5 years ago

As we discussed to create a new service instead of extending the current WPS, I have started a specification page in the wiki (as it will be used as documentation in future).
There are still many question (documented in the wiki), like how do we store perimeters of "code/value" type. You will also find an XML sample and an XSD schema.
Any comments are welcome !

You'll find the page here: https://forge.easysdi.org/projects/easysdi/wiki/4_shop_extraction_interface

#39 Updated by Villemagne Jérôme about 5 years ago

About the getOrders part of your wiki page.

As an order can have no thirdparty, min/maxOccurs were added into the xsd to the tierce organism.
I also added id and guid attributes to the tarification profile (change done also in db ^^).

Question:
- in the xsd, the product guid is named metadataId : why not guid ? (cf #643-41)

About the XSD validity : $myXML->schemaValidate($yourXSD) returns TRUE - so I would say that it's ok

#40 Updated by Mérour Xavier about 5 years ago

  • Assignee changed from Villemagne Jérôme to Blatti Yves

Assigned to Yves, he will answer asap.

#41 Updated by Villemagne Jérôme about 5 years ago

I made a mistake !

metadataId exposes the metadata's guid... so, to avoid any confusion, I propose the followig change to xsd:
- metadataId becomes guid and exposes the product guid
- product has a new children named metadata
- xml and pdf are moved into metadata
- metadata's id and guid are exposed as attributes

    <!-- Describes the products of a supplier -->         
    <xs:complexType name="productsType">
        <xs:sequence>
            <!-- There's at least one product -->  
            <xs:element name="product" maxOccurs="unbounded" minOccurs="1">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element type="xs:string" name="name"/>
                        <!-- Products pricing definition -->
                        <xs:element xmlns:sdi="http://www.easysdi.org/2011/sdi" type="sdi:productPricingType" name="pricing"/>
                        <!-- Product properties, this element is mandatory, but my be empty if no properties are set for this product -->
                        <xs:element xmlns:sdi="http://www.easysdi.org/2011/sdi" type="sdi:productPropertiesType" name="properties"/>
                        <!-- Product metadata -->
                        <xs:element xmlns:sdi="http://www.easysdi.org/2011/sdi" type="sdi:productMetadataType" name="metadata"/>

                    </xs:sequence>
                    <xs:attribute type="xs:integer" name="id" use="required"/>
                    <xs:attribute type="xs:string" name="guid" use="required"/>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    </xs:complexType>   

    <!-- Describes the metadata of a product -->
    <xs:complexType name="productMetadataType">
        <xs:sequence>
            <!-- XML copy of product's metadata, saved when order is placed -->
            <xs:element type="xs:base64Binary" name="xml"/>
            <!-- PDF copy of product's metadata, saved when order is placed -->
            <xs:element type="xs:base64Binary" name="pdf"/>
        </xs:sequence>
        <xs:attribute type="xs:integer" name="id" use="required"/>
        <xs:attribute type="xs:string" name="guid" use="required"/>
    </xs:complexType>

Btw: I also propose to add the client's name:

<!-- Describes a client (a user) -->
    <xs:complexType name="clientType">
        <xs:sequence>
            <xs:element type="xs:string" name="name"/>
            <!-- client's addresses -->
            <xs:element xmlns:sdi="http://www.easysdi.org/2011/sdi" type="sdi:contactType" name="contact"/>
            <xs:element xmlns:sdi="http://www.easysdi.org/2011/sdi" type="sdi:invoiceType" name="invoice"/>
            <xs:element xmlns:sdi="http://www.easysdi.org/2011/sdi" type="sdi:deliveryType" name="delivery"/>
            <xs:element xmlns:sdi="http://www.easysdi.org/2011/sdi" type="sdi:organismType" name="organism"/>
        </xs:sequence>
        <xs:attribute type="xs:integer" name="id" use="required"/>
        <xs:attribute type="xs:string" name="guid" use="required"/>
    </xs:complexType>

#42 Updated by Blatti Yves about 5 years ago

  • Assignee changed from Blatti Yves to Villemagne Jérôme

Looks perfect to me !

- Wow infos
- Such clearer

Can you merge your improvements in the wiki page attachments ?
https://forge.easysdi.org/projects/easysdi/wiki/4_shop_extraction_interface

#43 Updated by Villemagne Jérôme about 5 years ago

  • Assignee changed from Villemagne Jérôme to Blatti Yves

Update done for getOrders !

About setOrder:
with this method, we're allowed to set only one product at once (even if order has many products) : so why not call this method setProduct ?
if it's a remote storage, the supplier send a filename : why not if it's a local storage ?
what is the benefit using POST instead of XML/XSD ?
After some reflexions, we think we should use an XSD to validate the request cause:
- some parameters cannot be free filed (productState ...)
- an xsd validation protects us against unpredictable cases and allows a more concise/readable webservice's code
- it's more consistent (for us !) to have get and set using the same philosophy

#44 Updated by Blatti Yves about 5 years ago

  • Assignee changed from Blatti Yves to Villemagne Jérôme

- name setProduct : you're totally right about this !
- localStorage filename: I didn't set it as mandatory, because if we use the file post right: we set the content-disposition header with a filename (RFC:6266). But this can be discussed.
- I proposed using POST mainly for sending the data as a file and not XML encoded: like this PHP can stream the file to disk, and not load it to memory. This allows us to load extra-large files (the only limits are upload size in apache and php... and disk space).
- I agree that a full XML webservice would be more consistent, and "classic". But do we have a solution that would allow large files in XML response without blowing up server's memory?

#45 Updated by Blatti Yves about 5 years ago

#46 Updated by Villemagne Jérôme about 5 years ago

xml samples for setProduct requests and xsd : open for comments :-)

#47 Updated by Villemagne Jérôme about 5 years ago

As discussed with Yves, xml should be lighter:
- supplier is identified accross authentication
- we don't need id AND guid : I propose to drop id
- in the same way, we don't need id AND textual information (ie for state, storage...) : I propose to drop id

here is updated proposal files

#48 Updated by Villemagne Jérôme over 4 years ago

  • Description updated (diff)

#49 Updated by Magoni Bruno over 4 years ago

  • Target version changed from 158 to 4.3.0

#50 Updated by Villemagne Jérôme over 4 years ago

  • Status changed from Affected to Resolved

#51 Updated by Villemagne Jérôme over 4 years ago

  • Status changed from Resolved to Closed

#52 Updated by Van Hoecke Hélène about 4 years ago

  • Assignee deleted (Blatti Yves)

Also available in: Atom PDF