<commit_before>const Therocktrading = require('npm-therocktrading');
const _ = require('lodash');
const moment = require('moment');
const retry = require('../exchangeUtils').retry;
const marketData = require("./therocktrading-markets.json");

const QUERY_DELAY = 350;

const Trader = function(config) {
  this.post_only = true;
  this.use_sandbox = false;
  this.name = 'Therocktrading';
  this.scanback = true;
  this.scanbackTid = 0;
  this.since = null;
  this.asset = config.asset;
  this.currency = config.currency;

  this.api_url = 'https://api.therocktrading.com';
  this.api_sandbox_url = 'https://api-staging.therocktrading.com';

  if (_.isObject(config)) {
    this.key = config.key;
    this.secret = config.secret;

    this.pair = [config.asset, config.currency].join('').toUpperCase();
    this.post_only =
      typeof config.post_only !== 'undefined' ? config.post_only : true;

    if (config.sandbox) {
      this.use_sandbox = config.sandbox;
    }

  }

  this.therocktrading = new Therocktrading(this.key, this.secret, 'Gekko Broker v' + require('../package.json').version);
};

const recoverableErrors = [
  'SOCKETTIMEDOUT',
  'TIMEDOUT',
  'CONNRESET',
  'CONNREFUSED',
  'NOTFOUND',
  'Rate limit exceeded',
  'Response code 5',
  'Therocktrading is currently under maintenance.',
  'HTTP 408 Error',
  'HTTP 504 Error',
  'HTTP 503 Error',
  'EHOSTUNREACH',
  'EAI_AGAIN',
  'ENETUNREACH'
];

const includes = (str, list) => {
  if(!_.isString(str))
    return false;

  return _.some(list, item => str.includes(item));
}

Trader.prototype.processResponse = function(method, next) {
  return (error, body) => {
    if (body && body.code) {
      error = new Error(`Error ${body.code}: ${body.msg}`);
    }

    if(!error && body && !_.isEmpty(body.message)) {
      error = new Error(body.message);
    }

    /*if(
      response &&
      response.statusCode < 200 &&
      response.statusCode >= 300
    ) {
      error = new Error(`Response code ${response.statusCode}`);
    }*/

    if(error) {
      if(_.isString(error)) {
        error = new Error(error);
      }

      if(includes(error.message, recoverableErrors)) {
        error.notFatal = true;
      }

      return next(error);
    }

    return next(undefined, body);
  }
};

Trader.prototype.getTrades = function(since, callback, descending) {
  const handle = (err, data) => {
    if (err) return callback(err);
    var trades = [];
    if (_.isArray(data.trades)) {
      trades = _.map(data.trades, function(trade) {
        return {
          tid: trade.id,
          price: trade.price,
          amount: trade.amount,
          date: moment.utc(trade.date).format('X')
        };
      });
    }

    callback(null, descending ? trades : trades.reverse());
  };

  if (moment.isMoment(since)) since = since.format();
  //if (moment.isMoment(to)) to = to.format();

  var options = {
    order: "DESC" // which is default at therock
  }
  if (since)
  	options.after = since;
  const fetch = cb => this.therocktrading.trades(this.pair, options, this.processResponse('getTrades', cb));
  retry(null, fetch, handle);
};

Trader.prototype.getTicker = function(callback) {
  const result = (err, data) => {
    if (err) return callback(err);
    callback(undefined, { bid: data.bid, ask: data.ask });
  };

  const fetch = cb =>
    this.therocktrading.ticker(this.pair, this.processResponse('getTicker', cb));
  retry(null, fetch, result);
};

Trader.prototype.getFee = function(callback) {
  const fee = 0.2;
  // should check for discounts ?
  callback(undefined, fee);
};

Trader.prototype.getPortfolio = function(callback) {
  const result = (err, data) => {
    if (err) return callback(err);
    var portfolio = data.balances.map(function(balance) {
      return {
        name: balance.currency.toUpperCase(),
        amount: parseFloat(balance.trading_balance),
      };
    });
    callback(undefined, portfolio);
  };

  const fetch = cb => this.therocktrading.balances(this.processResponse('getPortfolio', cb));
  retry(undefined, fetch, result);
};

Trader.prototype.buy = function(amount, price, callback) {
  const handle = (err, result) => {
    if(err) {
      return callback(err);
    }
    callback(undefined, result.id);
  }
  const fetch = next => {
    this.therocktrading.buy(this.pair, amount.toString(), price.toString(), this.processResponse('order', next))
  };
  retry(null, fetch, handle);
}

Trader.prototype.sell = function(amount, price, callback) {
  const handle = (err, result) => {
    if(err) {
      return callback(err);
    }
    callback(undefined, result.id);
  }
  const fetch = next => {
    this.therocktrading.sell(this.pair, amount.toString(), price.toString(), this.processResponse('order', next))
  };
  retry(null, fetch, handle);
}

Trader.prototype.getOrder = function(order, callback) {
  const handle = (err, result) => {
    if(err)
      return callback(err);

    let price = 0;
    let amount = 0;
    let date = moment(0);

    if(result.amount === result.amount_unfilled) {
      return callback(null, {price, amount, date});
    }

    _.each(result.trades, trade => {
      date = moment(trade.date);
      price = ((price * amount) + (+trade.price * trade.amount)) / (+trade.amount + amount);
      amount += +trade.amount;
    });

    const fees = {};
    const feePercent = this.makerFee;

    const fee = price * amount * feePercent;
    fees[this.currency] = fee;

    callback(err, {price, amount, date, fees, feePercent});
  };

  const fetch = cb => this.therocktrading.order_status(this.pair, order, this.processResponse('order_status', cb));
  retry(null, fetch, handle);
};

Trader.prototype.checkOrder = function(order, callback) {
  const result = (err, data) => {
    if (err) return callback(err);

    var status = data.status;
    if (status == 'executed') {
      return callback(undefined, { executed: true, open: false, filledAmount: parseFloat(data.amount) });
    } else if (status === 'deleted') {
      return callback(undefined, { executed: false, open: false, filledAmount: parseFloat(data.amount - data.amount_unfilled) });
    } else if (status === 'active') {
      // should check if not expired (data.close_on)?
      return callback(undefined, { executed: false, open: true, filledAmount:  parseFloat(data.amount - data.amount_unfilled) });
    }

    callback(new Error('Unknown status ' + status));
  };

  const fetch = cb =>
    this.therocktrading.order_status(this.pair, order, this.processResponse('order_status', cb));
  retry(null, fetch, result);
};

Trader.prototype.cancelOrder = function(order, callback) {
  // callback for cancelOrder should be true if the order was already filled, otherwise false
  const result = (err, data) => {
    if(err) {
      return callback(null, true);  // need to catch the specific error but usually an error on cancel means it was filled
    }

    return callback(null, false);
  };

  const fetch = cb =>
    this.therocktrading.cancel_order(this.pair, order, this.processResponse('cancel_order', cb));
  retry(null, fetch, result);
};

Trader.prototype.roundAmount = function(amount) {
  return _.floor(amount, 8);
}

Trader.prototype.roundPrice = function(price) {
  return +price;
}

Trader.getCapabilities = function() {
  return {
    name: 'Therocktrading',
    slug: 'therocktrading',
    currencies: marketData.currencies,
    assets: marketData.assets,
    markets: marketData.markets,
    requires: ['key', 'secret'],
    providesHistory: 'date',
    providesFullHistory: true,
    // following tid, define type to use on trade to batch trades
    tid: 'tid',
    tradable: true,
    gekkoBroker: 0.6
  };
};

module.exports = Trader;

<commit_msg>refer to correct npm dep<commit_after>const Therocktrading = require('therocktrading');
const _ = require('lodash');
const moment = require('moment');
const retry = require('../exchangeUtils').retry;
const marketData = require("./therocktrading-markets.json");

const QUERY_DELAY = 350;

const Trader = function(config) {
  this.post_only = true;
  this.use_sandbox = false;
  this.name = 'Therocktrading';
  this.scanback = true;
  this.scanbackTid = 0;
  this.since = null;
  this.asset = config.asset;
  this.currency = config.currency;

  this.api_url = 'https://api.therocktrading.com';
  this.api_sandbox_url = 'https://api-staging.therocktrading.com';

  if (_.isObject(config)) {
    this.key = config.key;
    this.secret = config.secret;

    this.pair = [config.asset, config.currency].join('').toUpperCase();
    this.post_only =
      typeof config.post_only !== 'undefined' ? config.post_only : true;

    if (config.sandbox) {
      this.use_sandbox = config.sandbox;
    }

  }

  this.therocktrading = new Therocktrading(this.key, this.secret, 'Gekko Broker v' + require('../package.json').version);
};

const recoverableErrors = [
  'SOCKETTIMEDOUT',
  'TIMEDOUT',
  'CONNRESET',
  'CONNREFUSED',
  'NOTFOUND',
  'Rate limit exceeded',
  'Response code 5',
  'Therocktrading is currently under maintenance.',
  'HTTP 408 Error',
  'HTTP 504 Error',
  'HTTP 503 Error',
  'EHOSTUNREACH',
  'EAI_AGAIN',
  'ENETUNREACH'
];

const includes = (str, list) => {
  if(!_.isString(str))
    return false;

  return _.some(list, item => str.includes(item));
}

Trader.prototype.processResponse = function(method, next) {
  return (error, body) => {
    if (body && body.code) {
      error = new Error(`Error ${body.code}: ${body.msg}`);
    }

    if(!error && body && !_.isEmpty(body.message)) {
      error = new Error(body.message);
    }

    /*if(
      response &&
      response.statusCode < 200 &&
      response.statusCode >= 300
    ) {
      error = new Error(`Response code ${response.statusCode}`);
    }*/

    if(error) {
      if(_.isString(error)) {
        error = new Error(error);
      }

      if(includes(error.message, recoverableErrors)) {
        error.notFatal = true;
      }

      return next(error);
    }

    return next(undefined, body);
  }
};

Trader.prototype.getTrades = function(since, callback, descending) {
  const handle = (err, data) => {
    if (err) return callback(err);
    var trades = [];
    if (_.isArray(data.trades)) {
      trades = _.map(data.trades, function(trade) {
        return {
          tid: trade.id,
          price: trade.price,
          amount: trade.amount,
          date: moment.utc(trade.date).format('X')
        };
      });
    }

    callback(null, descending ? trades : trades.reverse());
  };

  if (moment.isMoment(since)) since = since.format();
  //if (moment.isMoment(to)) to = to.format();

  var options = {
    order: "DESC" // which is default at therock
  }
  if (since)
  	options.after = since;
  const fetch = cb => this.therocktrading.trades(this.pair, options, this.processResponse('getTrades', cb));
  retry(null, fetch, handle);
};

Trader.prototype.getTicker = function(callback) {
  const result = (err, data) => {
    if (err) return callback(err);
    callback(undefined, { bid: data.bid, ask: data.ask });
  };

  const fetch = cb =>
    this.therocktrading.ticker(this.pair, this.processResponse('getTicker', cb));
  retry(null, fetch, result);
};

Trader.prototype.getFee = function(callback) {
  const fee = 0.2;
  // should check for discounts ?
  callback(undefined, fee);
};

Trader.prototype.getPortfolio = function(callback) {
  const result = (err, data) => {
    if (err) return callback(err);
    var portfolio = data.balances.map(function(balance) {
      return {
        name: balance.currency.toUpperCase(),
        amount: parseFloat(balance.trading_balance),
      };
    });
    callback(undefined, portfolio);
  };

  const fetch = cb => this.therocktrading.balances(this.processResponse('getPortfolio', cb));
  retry(undefined, fetch, result);
};

Trader.prototype.buy = function(amount, price, callback) {
  const handle = (err, result) => {
    if(err) {
      return callback(err);
    }
    callback(undefined, result.id);
  }
  const fetch = next => {
    this.therocktrading.buy(this.pair, amount.toString(), price.toString(), this.processResponse('order', next))
  };
  retry(null, fetch, handle);
}

Trader.prototype.sell = function(amount, price, callback) {
  const handle = (err, result) => {
    if(err) {
      return callback(err);
    }
    callback(undefined, result.id);
  }
  const fetch = next => {
    this.therocktrading.sell(this.pair, amount.toString(), price.toString(), this.processResponse('order', next))
  };
  retry(null, fetch, handle);
}

Trader.prototype.getOrder = function(order, callback) {
  const handle = (err, result) => {
    if(err)
      return callback(err);

    let price = 0;
    let amount = 0;
    let date = moment(0);

    if(result.amount === result.amount_unfilled) {
      return callback(null, {price, amount, date});
    }

    _.each(result.trades, trade => {
      date = moment(trade.date);
      price = ((price * amount) + (+trade.price * trade.amount)) / (+trade.amount + amount);
      amount += +trade.amount;
    });

    const fees = {};
    const feePercent = this.makerFee;

    const fee = price * amount * feePercent;
    fees[this.currency] = fee;

    callback(err, {price, amount, date, fees, feePercent});
  };

  const fetch = cb => this.therocktrading.order_status(this.pair, order, this.processResponse('order_status', cb));
  retry(null, fetch, handle);
};

Trader.prototype.checkOrder = function(order, callback) {
  const result = (err, data) => {
    if (err) return callback(err);

    var status = data.status;
    if (status == 'executed') {
      return callback(undefined, { executed: true, open: false, filledAmount: parseFloat(data.amount) });
    } else if (status === 'deleted') {
      return callback(undefined, { executed: false, open: false, filledAmount: parseFloat(data.amount - data.amount_unfilled) });
    } else if (status === 'active') {
      // should check if not expired (data.close_on)?
      return callback(undefined, { executed: false, open: true, filledAmount:  parseFloat(data.amount - data.amount_unfilled) });
    }

    callback(new Error('Unknown status ' + status));
  };

  const fetch = cb =>
    this.therocktrading.order_status(this.pair, order, this.processResponse('order_status', cb));
  retry(null, fetch, result);
};

Trader.prototype.cancelOrder = function(order, callback) {
  // callback for cancelOrder should be true if the order was already filled, otherwise false
  const result = (err, data) => {
    if(err) {
      return callback(null, true);  // need to catch the specific error but usually an error on cancel means it was filled
    }

    return callback(null, false);
  };

  const fetch = cb =>
    this.therocktrading.cancel_order(this.pair, order, this.processResponse('cancel_order', cb));
  retry(null, fetch, result);
};

Trader.prototype.roundAmount = function(amount) {
  return _.floor(amount, 8);
}

Trader.prototype.roundPrice = function(price) {
  return +price;
}

Trader.getCapabilities = function() {
  return {
    name: 'Therocktrading',
    slug: 'therocktrading',
    currencies: marketData.currencies,
    assets: marketData.assets,
    markets: marketData.markets,
    requires: ['key', 'secret'],
    providesHistory: 'date',
    providesFullHistory: true,
    // following tid, define type to use on trade to batch trades
    tid: 'tid',
    tradable: true,
    gekkoBroker: 0.6
  };
};

module.exports = Trader;

<|endoftext|>


<commit_before>'use strict'

const os = require('os')

exports['default'] = {
  servers: {
    web: (api) => {
      return {
        enabled: true,
        // HTTP or HTTPS?
        secure: false,
        // Passed to https.createServer if secure=true. Should contain SSL certificates
        serverOptions: {},
        // Should we redirect all traffic to the first host in this array if hte request header doesn't match?
        // i.e.: [ 'https://www.site.com' ]
        allowedRequestHosts: process.env.ALLOWED_HOSTS ? process.env.ALLOWED_HOSTS.split(',') : [],
        // Port or Socket Path
        port: process.env.PORT || 8080,
        // Which IP to listen on (use '0.0.0.0' for all; '::' for all on ipv4 and ipv6)
        // Set to `null` when listening to socket
        bindIP: '0.0.0.0',
        // Any additional headers you want actionhero to respond with
        httpHeaders: {
          'X-Powered-By': api.config.general.serverName,
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS, TRACE',
          'Access-Control-Allow-Headers': 'Content-Type'
        },
        // Route that actions will be served from; secondary route against this route will be treated as actions,
        //  IE: /api/?action=test == /api/test/
        urlPathForActions: 'api',
        // Route that static files will be served from;
        //  path (relative to your project root) to serve static content from
        //  set to `null` to disable the file server entirely
        urlPathForFiles: 'public',
        // When visiting the root URL, should visitors see 'api' or 'file'?
        //  Visitors can always visit /api and /public as normal
        rootEndpointType: 'file',
        // simple routing also adds an 'all' route which matches /api/:action for all actions
        simpleRouting: true,
        // queryRouting allows an action to be defined via a URL param, ie: /api?action=:action
        queryRouting: true,
        // The cache or (if etags are enabled) next-revalidation time to be returned for all flat files served from /public; defined in seconds
        flatFileCacheDuration: 60,
        // Add an etag header to requested flat files which acts as fingerprint that changes when the file is updated;
        // Client will revalidate the fingerprint at latest after flatFileCacheDuration and reload it if the etag (and therfore the file) changed
        // or continue to use the cached file if it's still valid
        enableEtag: true,
        // should we save the un-parsed HTTP POST/PUT payload to connection.rawConnection.params.rawBody?
        saveRawBody: false,
        // How many times should we try to boot the server?
        // This might happen if the port is in use by another process or the socketfile is claimed
        bootAttempts: 1,
        // Settings for determining the id of an http(s) request (browser-fingerprint)
        fingerprintOptions: {
          cookieKey: 'sessionID',
          toSetCookie: true,
          onlyStaticElements: false,
          settings: {
            path: '/',
            expires: 3600000
          }
        },
        // Options to be applied to incoming file uploads.
        //  More options and details at https://github.com/felixge/node-formidable
        formOptions: {
          uploadDir: os.tmpdir(),
          keepExtensions: false,
          maxFieldsSize: 1024 * 1024 * 20,
          maxFileSize: 1024 * 1024 * 200
        },
        // Should we pad JSON responses with whitespace to make them more human-readable?
        // set to null to disable
        padding: 2,
        // Options to configure metadata in responses
        metadataOptions: {
          serverInformation: true,
          requesterInformation: true
        },
        // When true, returnErrorCodes will modify the response header for http(s) clients if connection.error is not null.
        // You can also set connection.rawConnection.responseHttpCode to specify a code per request.
        returnErrorCodes: true,
        // should this node server attempt to gzip responses if the client can accept them?
        // this will slow down the performance of actionhero, and if you need this funcionality, it is recommended that you do this upstream with nginx or your load balancer
        compress: false,
        // options to pass to the query parser
        // learn more about the options @ https://github.com/hapijs/qs
        queryParseOptions: {}
      }
    }
  }
}

exports.production = {
  servers: {
    web: (api) => {
      return {
        padding: null,
        metadataOptions: {
          serverInformation: false,
          requesterInformation: false
        }
      }
    }
  }
}

exports.test = {
  servers: {
    web: (api) => {
      return {
        secure: false,
        port: 18080 + parseInt(process.env.JEST_WORKER_ID || 0),
        matchExtensionMime: true,
        metadataOptions: {
          serverInformation: true,
          requesterInformation: true
        }
      }
    }
  }
}
<commit_msg>Adding strict transport security header<commit_after>'use strict'

const os = require('os')

exports['default'] = {
  servers: {
    web: (api) => {
      return {
        enabled: true,
        // HTTP or HTTPS?
        secure: false,
        // Passed to https.createServer if secure=true. Should contain SSL certificates
        serverOptions: {},
        // Should we redirect all traffic to the first host in this array if hte request header doesn't match?
        // i.e.: [ 'https://www.site.com' ]
        allowedRequestHosts: process.env.ALLOWED_HOSTS ? process.env.ALLOWED_HOSTS.split(',') : [],
        // Port or Socket Path
        port: process.env.PORT || 8080,
        // Which IP to listen on (use '0.0.0.0' for all; '::' for all on ipv4 and ipv6)
        // Set to `null` when listening to socket
        bindIP: '0.0.0.0',
        // Any additional headers you want actionhero to respond with
        httpHeaders: {
          'X-Powered-By': api.config.general.serverName,
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS, TRACE',
          'Access-Control-Allow-Headers': 'Content-Type',
          'Strict-Transport-Security': 'max-age=31536000; includeSubDomains'
        },
        // Route that actions will be served from; secondary route against this route will be treated as actions,
        //  IE: /api/?action=test == /api/test/
        urlPathForActions: 'api',
        // Route that static files will be served from;
        //  path (relative to your project root) to serve static content from
        //  set to `null` to disable the file server entirely
        urlPathForFiles: 'public',
        // When visiting the root URL, should visitors see 'api' or 'file'?
        //  Visitors can always visit /api and /public as normal
        rootEndpointType: 'file',
        // simple routing also adds an 'all' route which matches /api/:action for all actions
        simpleRouting: true,
        // queryRouting allows an action to be defined via a URL param, ie: /api?action=:action
        queryRouting: true,
        // The cache or (if etags are enabled) next-revalidation time to be returned for all flat files served from /public; defined in seconds
        flatFileCacheDuration: 60,
        // Add an etag header to requested flat files which acts as fingerprint that changes when the file is updated;
        // Client will revalidate the fingerprint at latest after flatFileCacheDuration and reload it if the etag (and therfore the file) changed
        // or continue to use the cached file if it's still valid
        enableEtag: true,
        // should we save the un-parsed HTTP POST/PUT payload to connection.rawConnection.params.rawBody?
        saveRawBody: false,
        // How many times should we try to boot the server?
        // This might happen if the port is in use by another process or the socketfile is claimed
        bootAttempts: 1,
        // Settings for determining the id of an http(s) request (browser-fingerprint)
        fingerprintOptions: {
          cookieKey: 'sessionID',
          toSetCookie: true,
          onlyStaticElements: false,
          settings: {
            path: '/',
            expires: 3600000
          }
        },
        // Options to be applied to incoming file uploads.
        //  More options and details at https://github.com/felixge/node-formidable
        formOptions: {
          uploadDir: os.tmpdir(),
          keepExtensions: false,
          maxFieldsSize: 1024 * 1024 * 20,
          maxFileSize: 1024 * 1024 * 200
        },
        // Should we pad JSON responses with whitespace to make them more human-readable?
        // set to null to disable
        padding: 2,
        // Options to configure metadata in responses
        metadataOptions: {
          serverInformation: true,
          requesterInformation: true
        },
        // When true, returnErrorCodes will modify the response header for http(s) clients if connection.error is not null.
        // You can also set connection.rawConnection.responseHttpCode to specify a code per request.
        returnErrorCodes: true,
        // should this node server attempt to gzip responses if the client can accept them?
        // this will slow down the performance of actionhero, and if you need this funcionality, it is recommended that you do this upstream with nginx or your load balancer
        compress: false,
        // options to pass to the query parser
        // learn more about the options @ https://github.com/hapijs/qs
        queryParseOptions: {}
      }
    }
  }
}

exports.production = {
  servers: {
    web: (api) => {
      return {
        padding: null,
        metadataOptions: {
          serverInformation: false,
          requesterInformation: false
        }
      }
    }
  }
}

exports.test = {
  servers: {
    web: (api) => {
      return {
        secure: false,
        port: 18080 + parseInt(process.env.JEST_WORKER_ID || 0),
        matchExtensionMime: true,
        metadataOptions: {
          serverInformation: true,
          requesterInformation: true
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>odoo.define('pos_debt_notebook.pos', function (require) {
    "use strict";

    var models = require('point_of_sale.models');
    var screens = require('point_of_sale.screens');
    var core = require('web.core');
    var gui = require('point_of_sale.gui');
    var utils = require('web.utils');
    var Model = require('web.DataModel');

    var _t = core._t;
    var round_pr = utils.round_precision;

    var _super_posmodel = models.PosModel.prototype;
    models.PosModel = models.PosModel.extend({
        initialize: function (session, attributes) {
            var partner_model = _.find(this.models, function(model){ return model.model === 'res.partner'; });
            partner_model.fields.push('debt_type', 'debt', 'debt_limit');
            var journal_model = _.find(this.models, function(model){ return model.model === 'account.journal'; });
            journal_model.fields.push('debt');
            var product_model = _.find(this.models, function(model){ return model.model === 'product.product'; });
            product_model.fields.push('credit_product');
            return _super_posmodel.initialize.call(this, session, attributes);
        },
        push_order: function(order, opts){
            var self = this;
            var pushed = _super_posmodel.push_order.call(this, order, opts);
            var client = order && order.get_client();
            if (client){
                order.paymentlines.each(function(line){
                    var journal = line.cashregister.journal;
                    if (!journal.debt)
                        return;
                    var amount = line.get_amount();
                    client.debt += amount;
                });
            }
            return pushed;
        },
        _save_to_server: function (orders, options) {
            var self = this;
            var def = _super_posmodel._save_to_server.call(this, orders, options);
            var partner_ids = [];
            _.each(orders, function(o){
                if (o.data.updates_debt && o.data.partner_id)
                    partner_ids.push(o.data.partner_id);
            });
            partner_ids = _.unique(partner_ids);
            if (partner_ids.length){
                return def.then(function(server_ids){
                    self.reload_debts(partner_ids);
                    return server_ids;
                });
            }else{
                return def;
            }
        },
        reload_debts: function(partner_ids){
            // function is called whenever we need to update debt value from server
            var limit = 0; // download only new debt value
            limit = 10; //debug
            return this._load_debts(partner_ids, limit).then(function(data){
                //TODO
                console.log('partner debt', data);
            });
        },
        _load_debts: function(partner_ids, limit){
            return new Model('res.partner').call('debt_history', [partner_ids], {'limit': limit});
        }
    });

    var _super_order = models.Order.prototype;
    models.Order = models.Order.extend({
        updates_debt: function(){
            // wheither order update debt value
            return this.has_credit_product() || this.has_debt_journal();
        },
        has_debt_journal: function(){
            return this.paymentlines.any(function(line){
                    return line.cashregister.journal.debt;
                });
        },
        has_credit_product: function(){
            return this.orderlines.any(function(line){
                return line.product.credit_product;
            });
        },
        export_as_JSON: function(){
            var data = _super_order.export_as_JSON.apply(this, arguments);
            data.updates_debt = this.updates_debt();
            return data;
        },
        add_paymentline: function(cashregister) {
            this.assert_editable();
            var self = this;
            var journal = cashregister.journal;
            if (!this.get_client() && (this.has_credit_product() || journal.debt)){
                setTimeout(function(){
                    self.pos.gui.show_screen('clientlist');
                }, 30);
            }

            var newPaymentline = new models.Paymentline({},{order: this, cashregister: cashregister, pos: this.pos});
            if (cashregister.journal.debt){
                newPaymentline.set_amount(this.get_due_debt());
            } else if (cashregister.journal.type !== 'cash' || this.pos.config.iface_precompute_cash){
                newPaymentline.set_amount(this.get_due());
            }
            this.paymentlines.add(newPaymentline);
            this.select_paymentline(newPaymentline);
        },
        get_due_debt: function(paymentline) {
            var due = this.get_total_with_tax() - this.get_total_paid();
            if (paymentline) {
                due = this.get_total_with_tax();
                var lines = this.paymentlines.models;
                for (var i = 0; i < lines.length; i++) {
                    if (lines[i] === paymentline) {
                        break;
                    } else {
                        due -= lines[i].get_amount();
                    }
                }
            }
            return round_pr(due, this.pos.currency.rounding);
        }
    });

    screens.PaymentScreenWidget.include({
        validate_order: function(options) {
            var currentOrder = this.pos.get_order();
            var isDebt = false;
            var plines = currentOrder.get_paymentlines();
            var debt_amount = 0;
            for (var i = 0; i < plines.length; i++) {
                if (plines[i].cashregister.journal.debt) {
                    isDebt = true;
                    debt_amount += plines[i].amount;
                }
            }
            var client = currentOrder.get_client();
            if (isDebt && !client){
                this.gui.show_popup('error',{
                    'title': _t('Unknown customer'),
                    'body': _t('You cannot use Debt payment. Select customer first.'),
                });
                return;
            }
            if (currentOrder.has_credit_product() && !client){
                this.gui.show_popup('error',{
                    'title': _t('Unknown customer'),
                    'body': _t("Don't forget to specify Customer when sell Credits."),
                });
                return;
            }
            if(isDebt && currentOrder.get_orderlines().length === 0){
                this.gui.show_popup('error',{
                    'title': _t('Empty Order'),
                    'body': _t('There must be at least one product in your order before it can be validated. (Hint: you can use some dummy zero price product)'),
                });
                return;
            }
            if (debt_amount > 0 && client.debt + debt_amount > client.debt_limit) {
                this.gui.show_popup('error', {
                    'title': _t('Max Debt exceeded'),
                    'body': _t('You cannot sell products on credit to the customer, because his max debt value will be exceeded.')
                });
                return;
            }
            this.pos.gui.screen_instances.clientlist.partner_cache.clear_node(client.id);
            this._super(options);
        },

        pay_full_debt: function(){
            var order = this.pos.get_order();

            var debtjournal = false;
            _.each(this.pos.cashregisters, function(cashregister) {
                if (cashregister.journal.debt) {
                    debtjournal = cashregister;

                }

            });

            var paymentLines = order.get_paymentlines();
            if (paymentLines.length) {
                _.each(paymentLines.models, function(paymentLine) {
                    if (paymentLine.cashregister.journal.debt){
                        paymentLine.destroy();
                    }
                });
            }

            var newDebtPaymentline = new models.Paymentline({},{order: order, cashregister: debtjournal, pos: this.pos});
            newDebtPaymentline.set_amount(order.get_client().debt * -1);
            order.paymentlines.add(newDebtPaymentline);
            this.render_paymentlines();
        },

        /* I inherit the native is_paid() method below for the following reason:
        without this inherit, if you have a customer with a debt and he comes
        to pay his debt without buying new items:
        1) cashier selects "debt journal"
        2) cashier selects the customer and clicks on "Select Customer and Pay Full Debt"
        3) a dummy product is automatically added to the pos order and
           the debt journal is selected with a negative amount corresponding
           to the debt.
           AT THAT MOMENT, the "Validate" button is active...
           so the cashier can click on it by accident !
           He should not, because he still has to select the payment method used
           to pay the debt.
        This problem is linked to the fact that the native is_paid() method
        returns with the following code:
        return (currentOrder.getTotalTaxIncluded() < 0.000001
                            || currentOrder.getPaidTotal() + 0.000001 >= currentOrder.getTotalTaxIncluded());
        (cf odoo/addons/point_of_sale/static/src/js/screens.js line 1256)
        So is_paid() always returns True when
        "currentOrder.getTotalTaxIncluded() < 0.000001" which is the case
        in this scenario with a pos.order with the dummy product with price = 0
        I must say that I don't understand what is the use case behind the
        "currentOrder.getTotalTaxIncluded() < 0.000001" */
        is_paid: function(){
            var currentOrder = this.pos.get_order();
            return (currentOrder.getPaidTotal() + 0.000001 >= currentOrder.getTotalTaxIncluded());
        },
        customer_changed: function() {
            var self = this;
            var client = this.pos.get_client();
            var debt = 0;
            if (client) {
                debt = Math.round(client.debt * 100) / 100;
                if (client.debt_type == 'credit') {
                    debt = - debt;
                }
            }
            var $js_customer_name = this.$('.js_customer_name');
            var $pay_full_debt = this.$('.pay-full-debt');
            $js_customer_name.text(client ? client.name : _t('Customer'));
            $pay_full_debt.on('click', function() {self.pay_full_debt();});
            $pay_full_debt.addClass('oe_hidden');
            if (client && debt) {
                if (client.debt_type == 'debt') {
                    if (debt > 0) {
                        $pay_full_debt.removeClass('oe_hidden');
                        $js_customer_name.append(' [Debt: ' + debt + ']');
                    } else if (debt < 0) {
                        $js_customer_name.append(' [Debt: ' + debt + ']');
                    }
                } else if (client.debt_type == 'credit') {
                    if (debt > 0) {
                        $js_customer_name.append(' [Credit: ' + debt + ']');
                    } else if (debt < 0) {
                        $pay_full_debt.removeClass('oe_hidden');
                        $js_customer_name.append(' [Credit: ' + debt + ']');
                    }
                }
            }
        }
    });

    gui.Gui.prototype.screen_classes.filter(function(el) { return el.name == 'clientlist';})[0].widget.include({
        init: function(parent, options){
            this._super(parent, options);
            this.round = function(value) {
                return Math.round(value * 100) / 100;
            };
            this.check_user_in_group = function(group_id, groups) {
                return  $.inArray(group_id, groups) != -1;
            };
        },
        render_list: function(partners){
            var debt_type = partners ? partners[0].debt_type : '';
            if (debt_type == 'debt') {
                this.$('#client-list-credit').remove();
            } else if (debt_type == 'credit') {
                this.$('#client-list-debt').remove();
            }
            this._super(partners);
        },
        toggle_save_button: function(){
            this._super();
            var $pay_full_debt = this.$('#set-customer-pay-full-debt');
            var curr_client = this.pos.get_order().get_client();
            if (this.editing_client) {
                $pay_full_debt.addClass('oe_hidden');
            } else {
                if ((this.new_client && this.new_client.debt > 0) ||
                        (curr_client && curr_client.debt > 0 && !this.new_client)) {
                    $pay_full_debt.removeClass('oe_hidden');
                }else{
                    $pay_full_debt.addClass('oe_hidden');
                }
            }
        },

        show: function(){
            this._super();
            var self = this;
            this.$('#set-customer-pay-full-debt').click(function(){
                self.save_changes();
//                self.gui.back();
                if (self.new_client.debt <= 0) {
                    self.gui.show_popup('error',{
                        'title': _t('Error: No Debt'),
                        'body': _t('The selected customer has no debt.'),
                    });
                    return;
                }
                // if the order is empty, add a dummy product with price = 0
                var order = self.pos.get_order();
                if (order) {
                    var lastorderline = order.get_last_orderline();
                    if (lastorderline === null && self.pos.config.debt_dummy_product_id){
                        var dummy_product = self.pos.db.get_product_by_id(
                            self.pos.config.debt_dummy_product_id[0]);
                        order.add_product(dummy_product, {'price': 0});
                    }
                }

                // select debt journal
                var debtjournal = false;
                _.each(self.pos.cashregisters, function(cashregister) {
                    if (cashregister.journal.debt) {
                        debtjournal = cashregister;
                    }
                });

                // add payment line with amount = debt *-1
                var paymentLines = order.get_paymentlines();
                if (paymentLines.length) {
                    /* Delete existing debt line
                    Usefull for the scenario where a customer comes to
                    pay his debt and the user clicks on the "Debt journal"
                    which opens the partner list and then selects partner
                    and clicks on "Select Customer and Pay Full Debt" */
                    _.each(paymentLines.models, function(paymentLine) {
                        if (paymentLine.cashregister.journal.debt){
                            paymentLine.destroy();
                        }
                    });
                }

                var newDebtPaymentline = new models.Paymentline({},{order: order, cashregister: debtjournal, pos: self.pos});
                newDebtPaymentline.set_amount(self.new_client.debt * -1);
                order.paymentlines.add(newDebtPaymentline);
                self.gui.show_screen('payment');
            });
        },
        saved_client_details: function(partner_id){
            this.pos.gui.screen_instances.clientlist.partner_cache.clear_node(partner_id);
            this._super(partner_id);
        },
        reload_partners: function(){
            var self = this;
            return this._super().then(function () {
                self.render_list(self.pos.db.get_partners_sorted(1000));
            });
        }
    });
});
<commit_msg>[ADD] added queue to requests (#220)<commit_after>odoo.define('pos_debt_notebook.pos', function (require) {
    "use strict";

    var models = require('point_of_sale.models');
    var screens = require('point_of_sale.screens');
    var core = require('web.core');
    var gui = require('point_of_sale.gui');
    var utils = require('web.utils');
    var Model = require('web.DataModel');

    var _t = core._t;
    var round_pr = utils.round_precision;

    var _super_posmodel = models.PosModel.prototype;
    models.PosModel = models.PosModel.extend({
        initialize: function (session, attributes) {
            this.reload_debts_partner_ids = [];
            this.reload_debts_ready = $.when();
            var partner_model = _.find(this.models, function(model){ return model.model === 'res.partner'; });
            partner_model.fields.push('debt_type', 'debt', 'debt_limit');
            var journal_model = _.find(this.models, function(model){ return model.model === 'account.journal'; });
            journal_model.fields.push('debt');
            var product_model = _.find(this.models, function(model){ return model.model === 'product.product'; });
            product_model.fields.push('credit_product');
            return _super_posmodel.initialize.call(this, session, attributes);
        },
        push_order: function(order, opts){
            var self = this;
            var pushed = _super_posmodel.push_order.call(this, order, opts);
            var client = order && order.get_client();
            if (client){
                order.paymentlines.each(function(line){
                    var journal = line.cashregister.journal;
                    if (!journal.debt)
                        return;
                    var amount = line.get_amount();
                    client.debt += amount;
                });
            }
            return pushed;
        },
        _save_to_server: function (orders, options) {
            var self = this;
            var def = _super_posmodel._save_to_server.call(this, orders, options);
            var partner_ids = [];
            _.each(orders, function(o){
                if (o.data.updates_debt && o.data.partner_id)
                    partner_ids.push(o.data.partner_id);
            });
            partner_ids = _.unique(partner_ids);
            if (partner_ids.length){
                return def.then(function(server_ids){
                    self.reload_debts(partner_ids);
                    return server_ids;
                });
            }else{
                return def;
            }
        },
        reload_debts: function(partner_ids){
            // function is called whenever we need to update debt value from server
            var limit = 0; // download only new debt value
            limit = 10; //debug
            var self = this;
            this.reload_debts_partner_ids = this.reload_debts_partner_ids.concat(partner_ids);
            this.reload_debts_ready = this.reload_debts_ready.then(function(){
                if (self.reload_debts_partner_ids.length > 0) {
                    var old_reload_debts_partner_ids = self.reload_debts_partner_ids.slice();
                    self.reload_debts_partner_ids.splice(0);
                    return self._load_debts(old_reload_debts_partner_ids, limit).then(function (data) {
                        //TODO
                        console.log('partner debt', data);
                    }).fail(function () {
                        self.reload_debts_partner_ids = self.reload_debts_partner_ids.concat(old_reload_debts_partner_ids);
                    });
                }
            });
        },
        _load_debts: function(partner_ids, limit){
            return new Model('res.partner').call('debt_history', [partner_ids], {'limit': limit});
        }
    });

    var _super_order = models.Order.prototype;
    models.Order = models.Order.extend({
        updates_debt: function(){
            // wheither order update debt value
            return this.has_credit_product() || this.has_debt_journal();
        },
        has_debt_journal: function(){
            return this.paymentlines.any(function(line){
                    return line.cashregister.journal.debt;
                });
        },
        has_credit_product: function(){
            return this.orderlines.any(function(line){
                return line.product.credit_product;
            });
        },
        export_as_JSON: function(){
            var data = _super_order.export_as_JSON.apply(this, arguments);
            data.updates_debt = this.updates_debt();
            return data;
        },
        add_paymentline: function(cashregister) {
            this.assert_editable();
            var self = this;
            var journal = cashregister.journal;
            if (!this.get_client() && (this.has_credit_product() || journal.debt)){
                setTimeout(function(){
                    self.pos.gui.show_screen('clientlist');
                }, 30);
            }

            var newPaymentline = new models.Paymentline({},{order: this, cashregister: cashregister, pos: this.pos});
            if (cashregister.journal.debt){
                newPaymentline.set_amount(this.get_due_debt());
            } else if (cashregister.journal.type !== 'cash' || this.pos.config.iface_precompute_cash){
                newPaymentline.set_amount(this.get_due());
            }
            this.paymentlines.add(newPaymentline);
            this.select_paymentline(newPaymentline);
        },
        get_due_debt: function(paymentline) {
            var due = this.get_total_with_tax() - this.get_total_paid();
            if (paymentline) {
                due = this.get_total_with_tax();
                var lines = this.paymentlines.models;
                for (var i = 0; i < lines.length; i++) {
                    if (lines[i] === paymentline) {
                        break;
                    } else {
                        due -= lines[i].get_amount();
                    }
                }
            }
            return round_pr(due, this.pos.currency.rounding);
        }
    });

    screens.PaymentScreenWidget.include({
        validate_order: function(options) {
            var currentOrder = this.pos.get_order();
            var isDebt = false;
            var plines = currentOrder.get_paymentlines();
            var debt_amount = 0;
            for (var i = 0; i < plines.length; i++) {
                if (plines[i].cashregister.journal.debt) {
                    isDebt = true;
                    debt_amount += plines[i].amount;
                }
            }
            var client = currentOrder.get_client();
            if (isDebt && !client){
                this.gui.show_popup('error',{
                    'title': _t('Unknown customer'),
                    'body': _t('You cannot use Debt payment. Select customer first.'),
                });
                return;
            }
            if (currentOrder.has_credit_product() && !client){
                this.gui.show_popup('error',{
                    'title': _t('Unknown customer'),
                    'body': _t("Don't forget to specify Customer when sell Credits."),
                });
                return;
            }
            if(isDebt && currentOrder.get_orderlines().length === 0){
                this.gui.show_popup('error',{
                    'title': _t('Empty Order'),
                    'body': _t('There must be at least one product in your order before it can be validated. (Hint: you can use some dummy zero price product)'),
                });
                return;
            }
            if (debt_amount > 0 && client.debt + debt_amount > client.debt_limit) {
                this.gui.show_popup('error', {
                    'title': _t('Max Debt exceeded'),
                    'body': _t('You cannot sell products on credit to the customer, because his max debt value will be exceeded.')
                });
                return;
            }
            this.pos.gui.screen_instances.clientlist.partner_cache.clear_node(client.id);
            this._super(options);
        },

        pay_full_debt: function(){
            var order = this.pos.get_order();

            var debtjournal = false;
            _.each(this.pos.cashregisters, function(cashregister) {
                if (cashregister.journal.debt) {
                    debtjournal = cashregister;

                }

            });

            var paymentLines = order.get_paymentlines();
            if (paymentLines.length) {
                _.each(paymentLines.models, function(paymentLine) {
                    if (paymentLine.cashregister.journal.debt){
                        paymentLine.destroy();
                    }
                });
            }

            var newDebtPaymentline = new models.Paymentline({},{order: order, cashregister: debtjournal, pos: this.pos});
            newDebtPaymentline.set_amount(order.get_client().debt * -1);
            order.paymentlines.add(newDebtPaymentline);
            this.render_paymentlines();
        },

        /* I inherit the native is_paid() method below for the following reason:
        without this inherit, if you have a customer with a debt and he comes
        to pay his debt without buying new items:
        1) cashier selects "debt journal"
        2) cashier selects the customer and clicks on "Select Customer and Pay Full Debt"
        3) a dummy product is automatically added to the pos order and
           the debt journal is selected with a negative amount corresponding
           to the debt.
           AT THAT MOMENT, the "Validate" button is active...
           so the cashier can click on it by accident !
           He should not, because he still has to select the payment method used
           to pay the debt.
        This problem is linked to the fact that the native is_paid() method
        returns with the following code:
        return (currentOrder.getTotalTaxIncluded() < 0.000001
                            || currentOrder.getPaidTotal() + 0.000001 >= currentOrder.getTotalTaxIncluded());
        (cf odoo/addons/point_of_sale/static/src/js/screens.js line 1256)
        So is_paid() always returns True when
        "currentOrder.getTotalTaxIncluded() < 0.000001" which is the case
        in this scenario with a pos.order with the dummy product with price = 0
        I must say that I don't understand what is the use case behind the
        "currentOrder.getTotalTaxIncluded() < 0.000001" */
        is_paid: function(){
            var currentOrder = this.pos.get_order();
            return (currentOrder.getPaidTotal() + 0.000001 >= currentOrder.getTotalTaxIncluded());
        },
        customer_changed: function() {
            var self = this;
            var client = this.pos.get_client();
            var debt = 0;
            if (client) {
                debt = Math.round(client.debt * 100) / 100;
                if (client.debt_type == 'credit') {
                    debt = - debt;
                }
            }
            var $js_customer_name = this.$('.js_customer_name');
            var $pay_full_debt = this.$('.pay-full-debt');
            $js_customer_name.text(client ? client.name : _t('Customer'));
            $pay_full_debt.on('click', function() {self.pay_full_debt();});
            $pay_full_debt.addClass('oe_hidden');
            if (client && debt) {
                if (client.debt_type == 'debt') {
                    if (debt > 0) {
                        $pay_full_debt.removeClass('oe_hidden');
                        $js_customer_name.append(' [Debt: ' + debt + ']');
                    } else if (debt < 0) {
                        $js_customer_name.append(' [Debt: ' + debt + ']');
                    }
                } else if (client.debt_type == 'credit') {
                    if (debt > 0) {
                        $js_customer_name.append(' [Credit: ' + debt + ']');
                    } else if (debt < 0) {
                        $pay_full_debt.removeClass('oe_hidden');
                        $js_customer_name.append(' [Credit: ' + debt + ']');
                    }
                }
            }
        }
    });

    gui.Gui.prototype.screen_classes.filter(function(el) { return el.name == 'clientlist';})[0].widget.include({
        init: function(parent, options){
            this._super(parent, options);
            this.round = function(value) {
                return Math.round(value * 100) / 100;
            };
            this.check_user_in_group = function(group_id, groups) {
                return  $.inArray(group_id, groups) != -1;
            };
        },
        render_list: function(partners){
            var debt_type = partners ? partners[0].debt_type : '';
            if (debt_type == 'debt') {
                this.$('#client-list-credit').remove();
            } else if (debt_type == 'credit') {
                this.$('#client-list-debt').remove();
            }
            this._super(partners);
        },
        toggle_save_button: function(){
            this._super();
            var $pay_full_debt = this.$('#set-customer-pay-full-debt');
            var curr_client = this.pos.get_order().get_client();
            if (this.editing_client) {
                $pay_full_debt.addClass('oe_hidden');
            } else {
                if ((this.new_client && this.new_client.debt > 0) ||
                        (curr_client && curr_client.debt > 0 && !this.new_client)) {
                    $pay_full_debt.removeClass('oe_hidden');
                }else{
                    $pay_full_debt.addClass('oe_hidden');
                }
            }
        },

        show: function(){
            this._super();
            var self = this;
            this.$('#set-customer-pay-full-debt').click(function(){
                self.save_changes();
//                self.gui.back();
                if (self.new_client.debt <= 0) {
                    self.gui.show_popup('error',{
                        'title': _t('Error: No Debt'),
                        'body': _t('The selected customer has no debt.'),
                    });
                    return;
                }
                // if the order is empty, add a dummy product with price = 0
                var order = self.pos.get_order();
                if (order) {
                    var lastorderline = order.get_last_orderline();
                    if (lastorderline === null && self.pos.config.debt_dummy_product_id){
                        var dummy_product = self.pos.db.get_product_by_id(
                            self.pos.config.debt_dummy_product_id[0]);
                        order.add_product(dummy_product, {'price': 0});
                    }
                }

                // select debt journal
                var debtjournal = false;
                _.each(self.pos.cashregisters, function(cashregister) {
                    if (cashregister.journal.debt) {
                        debtjournal = cashregister;
                    }
                });

                // add payment line with amount = debt *-1
                var paymentLines = order.get_paymentlines();
                if (paymentLines.length) {
                    /* Delete existing debt line
                    Usefull for the scenario where a customer comes to
                    pay his debt and the user clicks on the "Debt journal"
                    which opens the partner list and then selects partner
                    and clicks on "Select Customer and Pay Full Debt" */
                    _.each(paymentLines.models, function(paymentLine) {
                        if (paymentLine.cashregister.journal.debt){
                            paymentLine.destroy();
                        }
                    });
                }

                var newDebtPaymentline = new models.Paymentline({},{order: order, cashregister: debtjournal, pos: self.pos});
                newDebtPaymentline.set_amount(self.new_client.debt * -1);
                order.paymentlines.add(newDebtPaymentline);
                self.gui.show_screen('payment');
            });
        },
        saved_client_details: function(partner_id){
            this.pos.gui.screen_instances.clientlist.partner_cache.clear_node(partner_id);
            this._super(partner_id);
        },
        reload_partners: function(){
            var self = this;
            return this._super().then(function () {
                self.render_list(self.pos.db.get_partners_sorted(1000));
            });
        }
    });
});
<|endoftext|>


<commit_before>'use strict';

const getChannelURL = require('ember-source-channel-url');

module.exports = function() {
  return Promise.all([
    getChannelURL('release'),
    getChannelURL('beta'),
    getChannelURL('canary')
  ]).then((urls) => {
    return {
      scenarios: [
        {
          name: 'ember-lts-2.18',
          env: {
            EMBER_OPTIONAL_FEATURES: JSON.stringify({ 'jquery-integration': true })
          },
          npm: {
            devDependencies: {
              '@ember/jquery': '^0.5.1',
              'ember-source': '~2.18.0'
            }
          }
        },
        {
          name: 'ember-lts-3.4',
          npm: {
            devDependencies: {
              'ember-source': '~3.4.0'
            }
          }
        },
        {
          name: 'ember-release',
          npm: {
            devDependencies: {
              'ember-source': urls[0]
            }
          }
        },
        {
          name: 'ember-beta',
          npm: {
            devDependencies: {
              'ember-source': urls[1]
            }
          }
        },
        {
          name: 'ember-canary',
          npm: {
            devDependencies: {
              'ember-source': urls[2]
            }
          }
        },
        // The default `.travis.yml` runs this scenario via `npm test`,
        // not via `ember try`. It's still included here so that running
        // `ember try:each` manually or from a customized CI config will run it
        // along with all the other scenarios.
        {
          name: 'ember-default',
          npm: {
            devDependencies: {}
          }
        },
        {
          name: 'ember-default-with-jquery',
          env: {
            EMBER_OPTIONAL_FEATURES: JSON.stringify({
              'jquery-integration': true
            })
          },
          npm: {
            devDependencies: {
              '@ember/jquery': '^0.5.1'
            }
          }
        }
      ]
    };
  });
};
<commit_msg>Add useYarn: true to ember-try config<commit_after>'use strict';

const getChannelURL = require('ember-source-channel-url');

module.exports = function() {
  return Promise.all([
    getChannelURL('release'),
    getChannelURL('beta'),
    getChannelURL('canary')
  ]).then((urls) => {
    return {
      scenarios: [
        {
          name: 'ember-lts-2.18',
          env: {
            EMBER_OPTIONAL_FEATURES: JSON.stringify({ 'jquery-integration': true })
          },
          npm: {
            devDependencies: {
              '@ember/jquery': '^0.5.1',
              'ember-source': '~2.18.0'
            }
          }
        },
        {
          name: 'ember-lts-3.4',
          npm: {
            devDependencies: {
              'ember-source': '~3.4.0'
            }
          }
        },
        {
          name: 'ember-release',
          npm: {
            devDependencies: {
              'ember-source': urls[0]
            }
          }
        },
        {
          name: 'ember-beta',
          npm: {
            devDependencies: {
              'ember-source': urls[1]
            }
          }
        },
        {
          name: 'ember-canary',
          npm: {
            devDependencies: {
              'ember-source': urls[2]
            }
          }
        },
        // The default `.travis.yml` runs this scenario via `npm test`,
        // not via `ember try`. It's still included here so that running
        // `ember try:each` manually or from a customized CI config will run it
        // along with all the other scenarios.
        {
          name: 'ember-default',
          npm: {
            devDependencies: {}
          }
        },
        {
          name: 'ember-default-with-jquery',
          env: {
            EMBER_OPTIONAL_FEATURES: JSON.stringify({
              'jquery-integration': true
            })
          },
          npm: {
            devDependencies: {
              '@ember/jquery': '^0.5.1'
            }
          }
        }
      ],
      useYarn: true
    };
  });
};
<|endoftext|>


<commit_before>import { Card, CardText, CardTitle } from 'react-toolbox';
import cx from 'classnames';

import style from './style';

function Allocation({ subject, location, teacher, lecture }) {
    const allocation = cx(style.allocation, {
       [style.lecture]: lecture
    });

    return (
        <Card className={allocation}>
            <CardTitle
                title={subject}
                subtitle={location}
                className={style.title}
            />
            <CardText
                children={teacher}
                className={style.text}
            />
        </Card>
    );
}

export default Allocation;<commit_msg>refactor(components): rename allocation props<commit_after>import { Card, CardText, CardTitle } from 'react-toolbox';
import cx from 'classnames';

import style from './style';

function Allocation({ activity, classroom, teacher, lecture }) {
    const allocation = cx(style.allocation, {
       [style.lecture]: lecture
    });

    return (
        <Card className={allocation}>
            <CardTitle
                title={activity}
                subtitle={classroom}
                className={style.title}
            />
            <CardText
                children={teacher}
                className={style.text}
            />
        </Card>
    );
}

export default Allocation;<|endoftext|>


<commit_before>import Food from '../model'
import co from 'co'

const ABSOLUTE_KEYMAP = {
  'name': 'name',
  'short_name': 'short_name',
  'campus': 'campus',
  'address': 'address',
  'lat': 'lat',
  'lng': 'lng',
  'tags': 'tags',
  'sunday': {
    'open': 'hours.sunday.open',
    'close': 'hours.sunday.close',
    'is_closed': 'hours.sunday.closed'
  },
  'monday': {
    'open': 'hours.monday.open',
    'close': 'hours.monday.close',
    'is_closed': 'hours.monday.closed'
  },
  'tuesday': {
    'open': 'hours.tuesday.open',
    'close': 'hours.tuesday.close',
    'is_closed': 'hours.tuesday.closed'
  },
  'wednesday': {
    'open': 'hours.wednesday.open',
    'close': 'hours.wednesday.close',
    'is_closed': 'hours.wednesday.closed'
  },
  'thursday': {
    'open': 'hours.thursday.open',
    'close': 'hours.thursday.close',
    'is_closed': 'hours.thursday.closed'
  },
  'friday': {
    'open': 'hours.friday.open',
    'close': 'hours.friday.close',
    'is_closed': 'hours.friday.closed'
  },
  'saturday': {
    'open': 'hours.saturday.open',
    'close': 'hours.saturday.close',
    'is_closed': 'hours.saturday.closed'
  }
}

export default function filter(req, res, next) {
  let q = req.query.q
  q = q.split(' AND ')

  let queries = 0

  let filter = { $and: q }

  for (let i = 0; i < filter.$and.length; i++) {
    filter.$and[i] = { $or: q[i].trim().split(' OR ')}

    for (let j = 0; j < filter.$and[i].$or.length; j++) {
      let part = filter.$and[i].$or[j].trim().split(':')
      let x = formatPart(part[0], part[1])

      if (x.isValid) {
        filter.$and[i].$or[j] = x.query
        queries++
      }
    }
  }
  if(queries > 0) {
    co(function* () {
      try {
        let docs = yield Food
          .find(filter, '-__v -_id -hours._id')
          .limit(req.query.limit)
          .skip(req.query.skip)
          .sort(req.query.sort)
          .exec()
        res.json(docs)
      } catch(e) {
        return next(e)
      }
    })
  }
}

function formatPart(key, part) {
  // Response format
  let response = {
    key: key,
    isValid: true,
    query: {}
  }

  // Checking if the start of the segment is an operator (-, >, <, .>, .<)
  if (part.indexOf('-') === 0) {
    // Negation
    part = {
      operator: '-',
      value: part.substring(1)
    }
  } else if (part.indexOf('>=') === 0) {
    part = {
      operator: '>=',
      value: part.substring(2)
    }
  } else if (part.indexOf('<=') === 0) {
    part = {
      operator: '<=',
      value: part.substring(2)
    }
  } else if (part.indexOf('>') === 0) {
    part = {
      operator: '>',
      value: part.substring(1)
    }
  } else if (part.indexOf('<') === 0) {
    part = {
      operator: '<',
      value: part.substring(1)
    }
  } else {
    part = {
      operator: undefined,
      value: part
    }
  }

  if (isNaN(parseFloat(part.value)) || !isFinite(part.value)) {
    // Is not a number
    part.value = part.value.substring(1, part.value.length - 1)
  } else {
    part.value = parseFloat(part.value)
  }

  if (['lat', 'lng'].indexOf(key) > -1) {
    // Numbers and arrays of Numbers

    if (part.operator === '-') {
      response.query[ABSOLUTE_KEYMAP[key]] = { $ne: part.value }
    } else if (part.operator === '>') {
      response.query[ABSOLUTE_KEYMAP[key]] = { $gt: part.value }
    } else if (part.operator === '<') {
      response.query[ABSOLUTE_KEYMAP[key]] = { $lt: part.value }
    } else if (part.operator === '>=') {
      response.query[ABSOLUTE_KEYMAP[key]] = { $gte: part.value }
    } else if (part.operator === '<=') {
      response.query[ABSOLUTE_KEYMAP[key]] = { $lte: part.value }
    } else {
      // Assume equality if no operator
      response.query[ABSOLUTE_KEYMAP[key]] = part.value
    }
  } else if ('open'.indexOf(key) > -1) {
    // open:"monday,tuesday(>9),wednesday(12|4),-sunday"
    let days = part.value.split(',')
    for (let i = 0; i < days.length; i++) {
      let day

      if (days[i].indexOf('(') > -1) {
        // Hour bound is provided
        day = days[i].substring(0, days[i].indexOf('('))
        let hours = days[i].substring(days[i].indexOf('(') + 1, days[i].length - 1)
        let hour, lower, upper

        if (hours.indexOf('|') > -1) {
          // Check if vendor is open between hour range
          lower = parseInt(hours.split('|')[1])
          upper = parseInt(hours.split('|')[0])
        } else if (hours.indexOf('>') > -1) {
          // Open after provided hour
          hour = parseInt(hours.slice(1))
        } else if (hours.indexOf('<') > -1) {
          // Open before provided hour
          hour = parseInt(hours.slice(1))
        } else {
          // Open at hour
          hour = parseInt(hours)
        }

        response.query[ABSOLUTE_KEYMAP[day]['open']] = { $lte: lower || hour }
        response.query[ABSOLUTE_KEYMAP[day]['close']] = { $gte: upper || hour }
      } else {
        // No hours provided, check if open at any time
        day = days[i]
        let val = true
        if (day.substr(0, 1) == '-') {
          day = day.substr(1)
          val = false
        }
        response.query[ABSOLUTE_KEYMAP[day]['is_closed']] = { $ne: val }
      }
    }
  } else if ('tags'.indexOf(key) > -1) {
    // tags:"pizza,indian,-coffee"
    let tags = part.value.split(',')

    let contains = []
    let notContains = []

    for (let i = 0; i < tags.length; i++) {
      let tag = tags[i]
      if (tag.substr(0, 1) == '-') {
        notContains.push(tag.substr(1))
      } else {
        contains.push(tag)
      }
    }
    response.query[ABSOLUTE_KEYMAP['tags']] = { $in: contains, $nin: notContains }
  } else {
    // Strings

    if (part.operator === '-') {
      response.query[ABSOLUTE_KEYMAP[key]] = {
        $regex: '^((?!' + escapeRe(part.value) + ').)*$',
        $options: 'i'
      }
    } else {
      response.query[ABSOLUTE_KEYMAP[key]] = { $regex: '(?i).*' + escapeRe(part.value) + '.*' }
    }
  }
  return response
}

function escapeRe(str) {
  return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')
}
<commit_msg>Fix no result on empty contains<commit_after>import Food from '../model'
import co from 'co'

const ABSOLUTE_KEYMAP = {
  'name': 'name',
  'short_name': 'short_name',
  'campus': 'campus',
  'address': 'address',
  'lat': 'lat',
  'lng': 'lng',
  'tags': 'tags',
  'sunday': {
    'open': 'hours.sunday.open',
    'close': 'hours.sunday.close',
    'is_closed': 'hours.sunday.closed'
  },
  'monday': {
    'open': 'hours.monday.open',
    'close': 'hours.monday.close',
    'is_closed': 'hours.monday.closed'
  },
  'tuesday': {
    'open': 'hours.tuesday.open',
    'close': 'hours.tuesday.close',
    'is_closed': 'hours.tuesday.closed'
  },
  'wednesday': {
    'open': 'hours.wednesday.open',
    'close': 'hours.wednesday.close',
    'is_closed': 'hours.wednesday.closed'
  },
  'thursday': {
    'open': 'hours.thursday.open',
    'close': 'hours.thursday.close',
    'is_closed': 'hours.thursday.closed'
  },
  'friday': {
    'open': 'hours.friday.open',
    'close': 'hours.friday.close',
    'is_closed': 'hours.friday.closed'
  },
  'saturday': {
    'open': 'hours.saturday.open',
    'close': 'hours.saturday.close',
    'is_closed': 'hours.saturday.closed'
  }
}

export default function filter(req, res, next) {
  let q = req.query.q
  q = q.split(' AND ')

  let queries = 0

  let filter = { $and: q }

  for (let i = 0; i < filter.$and.length; i++) {
    filter.$and[i] = { $or: q[i].trim().split(' OR ')}

    for (let j = 0; j < filter.$and[i].$or.length; j++) {
      let part = filter.$and[i].$or[j].trim().split(':')
      let x = formatPart(part[0], part[1])

      if (x.isValid) {
        filter.$and[i].$or[j] = x.query
        queries++
      }
    }
  }
  if(queries > 0) {
    co(function* () {
      try {
        let docs = yield Food
          .find(filter, '-__v -_id -hours._id')
          .limit(req.query.limit)
          .skip(req.query.skip)
          .sort(req.query.sort)
          .exec()
        res.json(docs)
      } catch(e) {
        return next(e)
      }
    })
  }
}

function formatPart(key, part) {
  // Response format
  let response = {
    key: key,
    isValid: true,
    query: {}
  }

  // Checking if the start of the segment is an operator (-, >, <, .>, .<)

  if (part.indexOf('-') === 0) {
    // Negation
    part = {
      operator: '-',
      value: part.substring(1)
    }
  } else if (part.indexOf('>=') === 0) {
    part = {
      operator: '>=',
      value: part.substring(2)
    }
  } else if (part.indexOf('<=') === 0) {
    part = {
      operator: '<=',
      value: part.substring(2)
    }
  } else if (part.indexOf('>') === 0) {
    part = {
      operator: '>',
      value: part.substring(1)
    }
  } else if (part.indexOf('<') === 0) {
    part = {
      operator: '<',
      value: part.substring(1)
    }
  } else {
    part = {
      operator: undefined,
      value: part
    }
  }

  if (isNaN(parseFloat(part.value)) || !isFinite(part.value)) {
    // Is not a number
    part.value = part.value.substring(1, part.value.length - 1)
  } else {
    part.value = parseFloat(part.value)
  }

  if (['lat', 'lng'].indexOf(key) > -1) {
    // Numbers and arrays of Numbers

    if (part.operator === '-') {
      response.query[ABSOLUTE_KEYMAP[key]] = { $ne: part.value }
    } else if (part.operator === '>') {
      response.query[ABSOLUTE_KEYMAP[key]] = { $gt: part.value }
    } else if (part.operator === '<') {
      response.query[ABSOLUTE_KEYMAP[key]] = { $lt: part.value }
    } else if (part.operator === '>=') {
      response.query[ABSOLUTE_KEYMAP[key]] = { $gte: part.value }
    } else if (part.operator === '<=') {
      response.query[ABSOLUTE_KEYMAP[key]] = { $lte: part.value }
    } else {
      // Assume equality if no operator
      response.query[ABSOLUTE_KEYMAP[key]] = part.value
    }
  } else if ('open'.indexOf(key) > -1) {
    let days = part.value.split(',')
    for (let i = 0; i < days.length; i++) {
      let day

      if (days[i].indexOf('(') > -1) {
        // Hour bound is provided
        day = days[i].substring(0, days[i].indexOf('('))
        let hours = days[i].substring(days[i].indexOf('(') + 1, days[i].length - 1)
        let hour, lower, upper

        if (hours.indexOf('|') > -1) {
          // Check if vendor is open between hour range
          lower = parseInt(hours.split('|')[1])
          upper = parseInt(hours.split('|')[0])
        } else if (hours.indexOf('>') > -1) {
          // Open after provided hour
          hour = parseInt(hours.slice(1))
        } else if (hours.indexOf('<') > -1) {
          // Open before provided hour
          hour = parseInt(hours.slice(1))
        } else {
          // Open at hour
          hour = parseInt(hours)
        }

        response.query[ABSOLUTE_KEYMAP[day]['open']] = { $lte: lower || hour }
        response.query[ABSOLUTE_KEYMAP[day]['close']] = { $gte: upper || hour }
      } else {
        // No hours provided, check if open at any time
        day = days[i]
        let val = true
        if (day.substr(0, 1) == '-') {
          day = day.substr(1)
          val = false
        }
        response.query[ABSOLUTE_KEYMAP[day]['is_closed']] = { $ne: val }
      }
    }
  } else if ('tags'.indexOf(key) > -1) {
    let tags = part.value.split(',')

    let contains = []
    let notContains = []

    for (let i = 0; i < tags.length; i++) {
      let tag = tags[i].trim().toLowerCase()
      if (tag[0] === '-') {
        notContains.push(escapeRe(tag.substr(1)))
      } else {
        contains.push(escapeRe(tag))
      }
    }

    if (contains.length > 0 || notContains.length > 0) {
      response.query[ABSOLUTE_KEYMAP['tags']] = {}

      if (contains.length > 0) {
        response.query[ABSOLUTE_KEYMAP['tags']]['$all'] = contains
      }

      if (notContains.length > 0) {
        response.query[ABSOLUTE_KEYMAP['tags']]['$nin'] = notContains
      }
    }

  } else {
    // Strings

    if (part.operator === '-') {
      response.query[ABSOLUTE_KEYMAP[key]] = {
        $regex: '^((?!' + escapeRe(part.value) + ').)*$',
        $options: 'i'
      }
    } else {
      response.query[ABSOLUTE_KEYMAP[key]] = { $regex: '(?i).*' + escapeRe(part.value) + '.*' }
    }
  }
  return response
}

function escapeRe(str) {
  return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')
}
<|endoftext|>


<commit_before> app.factory("userlistFactory", function (Restangular, $http, settingsFactory, notificationFactory) {
	var object = {};
	var activeUsers = Restangular.all('users/active');
	var suspendedUsers = Restangular.all('users/suspended');
	object.getActiveUsers = function() {
		return activeUsers.getList();
	};
	object.getSuspendedUsers = function() {
		return suspendedUsers.getList();
	};
	object.activateUser = function(username) {
		console.log("Activate", username);
		var usernameData = { Username: username };
		$http.post(settingsFactory.backendUrl + "user/activate", usernameData).success(function (data) {
			console.log("ActivateUser Rückgabe", data);
		}).error(function (data, status) {
			if (status == 401) {
				notificationFactory.error({content: "You are not logged in as administrator."});
			} else {
				notificationFactory.error({title: "Error:", content: "Server error occured with status code: " + status + " and reponse: " + data });
			}
		});
	};

	object.changeRole = function(userID, role) {
		console.log("Change Role", userID);
		var userData = {UserID: userID, Role: role};
		$http.put(settingsFactory.backendUrl+"user", userData).success(function(data) {
		console.log("ChangeUser userID Rückgabe",data);
		}).error(function(data, status) {
			notificationFactory.error({title:"Error:", content: "Server error occurred with status code:  "+status+" and response: "+data});
		});
	};
	return object;
});;
<commit_msg>Remove debug messages<commit_after> app.factory("userlistFactory", function (Restangular, $http, settingsFactory, notificationFactory) {
	var object = {};
	var activeUsers = Restangular.all('users/active');
	var suspendedUsers = Restangular.all('users/suspended');
	object.getActiveUsers = function() {
		return activeUsers.getList();
	};
	object.getSuspendedUsers = function() {
		return suspendedUsers.getList();
	};
	object.activateUser = function(username) {
		console.log("Activate", username);
		var usernameData = { Username: username };
		$http.post(settingsFactory.backendUrl + "user/activate", usernameData).success(function (data) {
			console.log("ActivateUser Rückgabe", data);
		}).error(function (data, status) {
			if (status == 401) {
				notificationFactory.error({content: "You are not logged in as administrator."});
			} else {
				notificationFactory.error({title: "Error:", content: "Server error occured with status code: " + status + " and reponse: " + data });
			}
		});
	};

	object.changeRole = function(userID, role) {
		var userData = {UserID: userID, Role: role};
		$http.put(settingsFactory.backendUrl+"user", userData).success(function(data) {
		}).error(function(data, status) {
			notificationFactory.error({title:"Error:", content: "Server error occurred with status code:  "+status+" and response: "+data});
		});
	};
	return object;
});;
<|endoftext|>


<commit_before>'use strict';

const getChannelURL = require('ember-source-channel-url');

module.exports = function() {
  return Promise.all([
    getChannelURL('release'),
    getChannelURL('beta'),
    getChannelURL('canary'),
  ]).then((urls) => {
    return {
      scenarios: [
        {
          name: 'ember-lts-2.12',
          npm: {
            devDependencies: {
              'ember-source': '~2.12.0'
            }
          }
        },
        {
          name: 'ember-lts-2.16',
          npm: {
            devDependencies: {
              'ember-source': '~2.16.0'
            }
          }
        },
        {
          name: 'ember-lts-2.18',
          npm: {
            devDependencies: {
              'ember-source': '~2.18.0'
            }
          }
        },
        {
          name: 'ember-release',
          npm: {
            devDependencies: {
              'ember-source': urls[0]
            }
          }
        },
        {
          name: 'ember-beta',
          npm: {
            devDependencies: {
              'ember-source': urls[1]
            }
          }
        },
        {
          name: 'ember-canary',
          npm: {
            devDependencies: {
              'ember-source': urls[2]
            }
          }
        },
        {
          name: 'ember-default',
          npm: {
            devDependencies: {}
          }
        }
      ]
    };
  });
};
<commit_msg>switching to yarn in ember-try<commit_after>'use strict';

const getChannelURL = require('ember-source-channel-url');

module.exports = function() {
  return Promise.all([
    getChannelURL('release'),
    getChannelURL('beta'),
    getChannelURL('canary'),
  ]).then((urls) => {
    return {
      useYarn: true,

      scenarios: [
        {
          name: 'ember-lts-2.12',
          npm: {
            devDependencies: {
              'ember-source': '~2.12.0'
            }
          }
        },
        {
          name: 'ember-lts-2.16',
          npm: {
            devDependencies: {
              'ember-source': '~2.16.0'
            }
          }
        },
        {
          name: 'ember-lts-2.18',
          npm: {
            devDependencies: {
              'ember-source': '~2.18.0'
            }
          }
        },
        {
          name: 'ember-release',
          npm: {
            devDependencies: {
              'ember-source': urls[0]
            }
          }
        },
        {
          name: 'ember-beta',
          npm: {
            devDependencies: {
              'ember-source': urls[1]
            }
          }
        },
        {
          name: 'ember-canary',
          npm: {
            devDependencies: {
              'ember-source': urls[2]
            }
          }
        },
        {
          name: 'ember-default',
          npm: {
            devDependencies: {}
          }
        }
      ]
    };
  });
};
<|endoftext|>