Module:Wikimédia France expenses
Appearance
From Meta, a Wikimedia project coordination wiki
This is an archived version of this page, as edited by 0x010C (talk | contribs) at 13:57, 2 July 2020. It may differ significantly from the current version .
Module documentation
[create]
You might want to create a documentation page for this Scribunto module.
Editors can experiment in this module’s sandbox (create | mirror) and testcases (create) pages.
Please add categories to the /doc subpage. Subpages of this module.
Editors can experiment in this module’s sandbox (create | mirror) and testcases (create) pages.
Please add categories to the /doc subpage. Subpages of this module.
local p = {} local STATES = { "request", "approval", "execution", "archiving", "verification" } local NEXT_STATE = { request = "approval", approval = "execution", execution = "archiving", archiving = "verification", verification = nil, abort = nil } local ALLOWED_USERS = { request = nil, approval = { ["Pyb"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, ["Pradigue"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, ["Rémy Gerbet WMFr"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, ["CindyDavidWMFr"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, ["0x010C"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, ["0x010D"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, }, execution = { ["Pyb"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, ["Pradigue"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, ["Rémy Gerbet WMFr"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, ["CindyDavidWMFr"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, ["0x010C"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, ["0x010D"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, }, archiving = { ["Jonathan Balima WMFr"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, ["0x010E"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, }, verification = { ["Pradigue"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, ["0x010D"] = os.time{year=2099, month=1, day=1, hour=0, min=0, sec=0}, } } ALLOWED_USERS["abort"] = ALLOWED_USERS["approval"] local USERS_WITH_LIMITATIONS = { ["Rémy Gerbet WMFr"] = 10000, ["CindyDavidWMFr"] = 10000, ["0x010C"] = 10000 } local BOX_TEMPLATE = 'Wikimédia_France_expenses/box' local STATE_TEMPLATE = 'Wikimédia France expenses/state' local SIMPLE_TEMPLATE = 'Wikimédia France expenses/simple' local SIMPLE_T_TEMPLATE = 'Wikimédia France expenses/simple-header' local SIMPLE_B_TEMPLATE = 'Wikimédia France expenses/simple-footer' local CREATE_TEMPLATE = 'Wikimédia France expenses/create' local DATA_PAGE = ':Wikimédia_France/Finances/Dépenses/' local USER_DATA_PAGE = 'WMFrDépenses.json' local STATE_CATEGORY = { request = "Wikimédia France/Finances/Dépense/À approuver", approval = "Wikimédia France/Finances/Dépense/À exécuter", execution = "Wikimédia France/Finances/Dépense/À archiver", archiving = "Wikimédia France/Finances/Dépense/À vérifier", verification = "Wikimédia France/Finances/Dépense/Traitée", abort = "Wikimédia France/Finances/Dépense/Abandonnée" } local DATE_CATEGORY = "Wikimédia France/Finances/Dépense" local ERROR_CATEGORY = "Wikimédia France/Finances/Dépense/Erreur de signature" local DATE_REGEX = "(%d+)-(%d+)-(%d+)" local lang = mw.language.new( 'fr' ) local signData = {} local categories = {} local function checkState( data, stateName ) local user = data.state[ stateName ].u local date = data.state[ stateName ].t if ALLOWED_USERS[stateName] ~= nil then allowedDate = ALLOWED_USERS[stateName][user] if allowedDate == nil or date > allowedDate then return "L'utilisateur " .. user .. " n'est pas autorisé à signer cette étape." elseif (stateName == 'approval' or stateName == 'execution') and user == data.state['request'].u then return "Le demandeur n'a pas le droit de signer cette étape." elseif USERS_WITH_LIMITATIONS[user] ~= nil and data.amount > USERS_WITH_LIMITATIONS[user] then return "Montant trop élevé pour pouvoir être signé par " .. user .. "." end end return nil end local function getState(value) result = STATES[1] if value.state['abort'] ~= nil and value.state['abort'].u ~= nil then return 'abort' end for _,stateName in pairs(STATES) do if value.state[stateName] == nil or value.state[stateName].u == nil then break end if checkState( value, stateName ) ~= nil then break end result = stateName end return result end local function getInfoById(data, id) for _,value in pairs(data) do if tonumber(value.id) == tonumber(id) then return value end end return nil end local function convert_date(date) year, month, day =s:match(date) return os.time{year=year, month=month, day=day, hour=0, min=0, sec=0} end local function fromDateFilter(data, date) filtered_data = {} -- convert date to timestamp year, month, day = date:match(DATE_REGEX) date = os.time{year=year, month=month, day=day, hour=0, min=0, sec=0} for _,value in pairs(data) do if value.state.request.t >= date then table.insert(filtered_data, value) end end return filtered_data end local function toDateFilter(data, date) filtered_data = {} -- convert date to timestamp year, month, day = date:match(DATE_REGEX) date = os.time{year=year, month=month, day=day, hour=23, min=59, sec=59} for _,value in pairs(data) do if value.state.request.t <= date then table.insert(filtered_data, value) end end return filtered_data end local function nameFilter(data, name) filtered_data = {} name = mw.ustring.lower(mw.text.trim(name)) for _,value in pairs(data) do if mw.ustring.find( mw.ustring.lower(value.name), name ) ~= nil then table.insert(filtered_data, value) end end return filtered_data end local function reasonFilter(data, reason) filtered_data = {} reason = mw.ustring.lower(mw.text.trim(reason)) for _,value in pairs(data) do if mw.ustring.find( mw.ustring.lower(value.reason), reason ) ~= nil then table.insert(filtered_data, value) end end return filtered_data end local function minAmountFilter(data, amount) filtered_data = {} for _,value in pairs(data) do if value.amount >= tonumber(amount) then table.insert(filtered_data, value) end end return filtered_data end local function maxAmountFilter(data, amount) filtered_data = {} for _,value in pairs(data) do if value.amount <= tonumber(amount) then table.insert(filtered_data, value) end end return filtered_data end local function projectFilter(data, projects) if #projects == 0 then return data end filtered_data = {} for _,value in pairs(data) do for _,project in pairs(projects) do project = mw.ustring.lower(mw.text.trim(project)) if value.project ~= nil then if mw.ustring.find( mw.ustring.lower(value.project), project ) ~= nil then table.insert(filtered_data, value) end end end end return filtered_data end local function stateFilter(data, stateNames) if #stateNames == 0 then return data end filtered_data = {} for _,value in pairs(data) do currStateName = getState(value) for _, name in ipairs(stateNames) do if mw.ustring.lower(mw.text.trim(name)) == currStateName then table.insert(filtered_data, value) break end end end return filtered_data end local function getDigitalSignature(frame, user, id, stateName) local sign local title = mw.title.new( "User:" .. user .. '/' .. USER_DATA_PAGE ) if title.exists then if signData[user] == nil then signData[user] = mw.text.jsonDecode(frame:expandTemplate{ title = "User:" .. user .. '/' .. USER_DATA_PAGE, args = {} }) end if signData[user][id] ~= nil then return signData[user][id][stateName] elseif signData[user][tonumber(id)] ~= nil then return signData[user][tonumber(id)][stateName] end end table.insert( categories, ERROR_CATEGORY ) return nil end local function _expense(frame, data, main_template, state_template) local states = "" local currentState = getState( data ) local nextStateToSign = NEXT_STATE[ currentState ] if state_template ~= nil then for key,stateName in pairs(STATES) do local user = nil local date = nil local sign = nil local baduser = nil local isSelected = nil if stateName == 'approval' and data.state["abort"] ~= nil then stateName = 'abort' end if data.state[stateName] ~= nil then user = data.state[stateName].u date = data.state[stateName].t end if user ~= nil then -- get digital signature from user json subpage sign = getDigitalSignature(frame, user, data.id, stateName) -- check if the user is allowed to sign that state baduser = checkState( data, stateName ) if baduser ~= nil then table.insert( categories, ERROR_CATEGORY ) end end if stateName == nextStateToSign or ( stateName == 'abort' and baduser ~= nil ) then isSelected = "true" end states = states .. frame:expandTemplate{ title = state_template, args = { nb = key, name = stateName, user = user, date = lang:formatDate( "j F Y à H:i (e)", os.date("%Y%m%d%H%M00", date), false), timestamp = date, sign = sign, baduser = baduser, selected = isSelected, } } if stateName == 'abort' then break end end end table.insert( categories, STATE_CATEGORY[ currentState ] ) table.insert( categories, DATE_CATEGORY ) return frame:expandTemplate{ title = main_template, args = { id = data.id, date = os.date("%Y-%m-%d", data.state.request.t), applicant = data.state.request.u, name = data.name, project = data.project, reason = data.reason, amount = data.amount, comment = data.comment, state = currentState, states = states } } end local function dateToPageName(timestamp) local year = tonumber( os.date('%Y', timestamp) ) local month = tonumber( os.date('%m', timestamp) ) if month < 7 then year = year - 1 end return DATA_PAGE .. tostring( year ) .. '-' .. tostring( year + 1 ) .. '.json' end function p.main(frame) local args = frame:getParent().args local format = args.format local search = args.search local create = args.create local id = args.id local exercice = tonumber(args.exercice) local name = args.name local reason = args.reason local project = (args.project == nil or args.project == "") and {} or mw.text.split(args.project, ",", true ) local dateFrom = args.from local dateTo = args.to local amountMin = args.min local amountMax = args.max local state = (args.state == nil or args.state == "") and {} or mw.text.split( args.state, ",", true ) local searchOptions = '' local text = '' if format ~= nil then searchOptions = 'data-format="' .. format .. '"' end if id ~= nil then searchOptions = searchOptions .. ' data-id="' .. id .. '"' local data = mw.text.jsonDecode(frame:expandTemplate{ title = dateToPageName(id), args = {} }) data = getInfoById( data, id ) if data ~= nil then text = _expense(frame, data, BOX_TEMPLATE, STATE_TEMPLATE) local categoriesString = "" for _,cat in pairs(categories) do categoriesString = categoriesString .. "\n[[Category:" .. cat .. "/" .. os.date("%Y", data.state.request.t) .. "]]" end return text .. "\n" .. categoriesString end elseif exercice ~= nil then searchOptions = searchOptions .. ' data-exercice="' .. exercice .. '"' local data = mw.text.jsonDecode(frame:expandTemplate{ title = DATA_PAGE .. tostring( exercice ) .. '-' .. tostring( exercice + 1 ) .. '.json', args = {} }) if #project > 0 then searchOptions = searchOptions .. ' data-project="' .. args.project .. '"' data = projectFilter(data, project) end if dateFrom ~= nil then searchOptions = searchOptions .. ' data-from="' .. dateFrom .. '"' data = fromDateFilter(data, dateFrom) end if dateTo ~= nil then searchOptions = searchOptions .. ' data-to="' .. dateTo .. '"' data = toDateFilter(data, dateTo) end if name ~= nil then searchOptions = searchOptions .. ' name="' .. name .. '"' data = nameFilter(data, name) end if reason ~= nil then searchOptions = searchOptions .. ' reason="' .. reason .. '"' data = reasonFilter(data, reason) end if amountMin ~=nil then searchOptions = searchOptions .. ' data-min="' .. amountMin .. '"' data = minAmountFilter(data, amountMin) end if amountMax ~=nil then searchOptions = searchOptions .. ' data-max="' .. amountMax .. '"' data = maxAmountFilter(data, amountMax) end if #state > 0 then searchOptions = searchOptions .. ' data-state="' .. args.state .. '"' data = stateFilter(data, state) end if format == 'simple' then text = text .. frame:expandTemplate{ title = SIMPLE_T_TEMPLATE, args = {} } .. '\n' if #data > 0 then for _,value in pairs(data) do text = text .. _expense(frame, value, SIMPLE_TEMPLATE, nil) .. '\n' end else text = text .. '|-\n| colspan="8" | Aucune dépense correspondant aux critères.\n' end text = text .. frame:expandTemplate{ title = SIMPLE_B_TEMPLATE, args = {} } else for _,value in pairs(data) do text = text .. _expense(frame, value, BOX_TEMPLATE, STATE_TEMPLATE) .. "<br>" end end end if search ~= nil then text = '<div class="eb-search"><div class="eb-search-inputs" ' .. searchOptions .. '></div><div class="eb-search-results">\n' .. text .. '\n</div></div>' end if create ~= nil then text = text .. '\n' .. frame:expandTemplate{ title = CREATE_TEMPLATE, args = {} } end return text end return p