Skip to content

Instantly share code, notes, and snippets.

@rheaditi
Last active June 21, 2021 16:19
Show Gist options
  • Save rheaditi/35a60f124530667870dd60afa2e52ffa to your computer and use it in GitHub Desktop.
Save rheaditi/35a60f124530667870dd60afa2e52ffa to your computer and use it in GitHub Desktop.
Download all Payslips from Keka HR Software
/**
* This puppeteer script will automatically download all payslips for you, if your company uses Keka HR Software.
* I downloaded about 4 years of payslips in approximately 2 minutes! :P
*
* - Download this file and save it in some temporary directory.
* - Fill in the params wherever you see the ℹ️ in this file & save.
* - Install the dependency `puppeteer-core` (just run `npm init` and `npm install --save puppeteer-core@2` in your directory).
* - Requires NodeJS >= 12 (tested with v12.13.1), puppeteer-core@2 & Google Chrome browser.
* - Run: `node downloadPayslips.js`
* - The files are all saved to the browser's default download folder.
* - If there are any missing months, or if it happens to encounter any error, the script will take a screenshot & exit.
*
* License: MIT
*/
const puppeteer = require('puppeteer-core');
const data = {
/**
* 📝
* Manually copy the cookies over from your existing logged in session from your company's Keka HR Software.
* This allows access to your keka account so, goes without saying, to be careful with where these cookies are shared! 🤓
*/
cookies: [
{
"name": "__RequestVerificationToken",
"value": "<PASTE_VALUE_HERE>", // <-- PASTE HERE ℹ️
"httpOnly": true
},
{
"name": "ASP.NET_SessionId",
"value": "<PASTE_VALUE_HERE>", // <-- PASTE HERE ℹ️
"httpOnly": true
},
{
"name": ".AspNet.Cookies",
"value": "<PASTE_VALUE_HERE>", // <-- PASTE HERE ℹ️
"httpOnly": true,
"secure": true
}
],
/**
* 📝
* Parameters of which months of payslips to be downloaded. Both start & end periods are inclusive.
* Other parameters required by this script.
*/
params: {
startYear: 2020, startMonth: 1, // <-- set the start year and month here (months start from 1 (Jan)) ℹ️
endYear: 2020, endMonth: 3, // <-- set the end year and month here (months start from 1 (Jan)) ℹ️
chromePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' // path to Chrome browser installed on your system, the default here is for MacOS, change accordingly ℹ️
kekaDomain: 'example.keka.com', // the subdomain of your company on the keka.com website. ℹ️
},
};
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const getIterations = (options) => {
const { startYear, startMonth, endYear, endMonth } = options;
let totalIterations = [];
for (let year = startYear; year <= endYear; year++) {
let currentMonths = months.map((month, index) => ({
year,
month: index + 1,
name: month,
}));
if (year === startYear) {
currentMonths = currentMonths.filter(month => month.month >= startMonth);
}
if (year === endYear) {
currentMonths = currentMonths.filter(month => month.month <= endMonth);
}
totalIterations.push({
year,
months: currentMonths,
});
}
return totalIterations;
}
const iterations = getIterations(data.params);
const download = async () => {
const browser = await puppeteer.launch({
headless: false,
executablePath: data.params.chromePath,
devtools: false,
});
const page = await browser.newPage();
page.setViewport({
width: 1366,
height: 768,
deviceScaleFactor: 1
});
// setup
await page.setCookie(...data.cookies.map(cookie => ({ ...cookie, domain: data.params.kekaDomain })));
await page.goto(`https://${params.kekaDomain}/old/#/finances/mypay/payslips`, { waitUntil: 'networkidle2' });
for (let iteration of iterations) {
const { year, months } = iteration;
console.log(`running ${months.length} iterations for year ${year}`);
console.table(months);
// select year
await page.click('.pay-slip-year > div > button');
const [yearItem] = await page.$x(`//a[contains(.,${year})]`);
if (!yearItem) {
console.warn(`Unable to select year ${year}`);
page.screenshot({ path: `${year}--not-found.png` });
continue;
}
await yearItem.click();
await page.waitFor(5000);
// select each month
for (let month of months) {
// select month
const { name } = month;
const [monthButton] = await page.$x(`//a[contains(.,\'${name}\')]`);
if (!monthButton) {
console.warn(`Unable to select month ${year}-${month}`);
await page.screenshot({ path: `${year}-${name}-not-found.png` });
continue;
}
await monthButton.click();
await page.waitFor(1000);
// ensure correct month
const headings = await page.$x(`//h2[contains(.,'${name} ${year}')]`);
if (!headings.length) {
console.warn(`Month heading not found`);
await page.screenshot({ path: `${year}-${name}-heading-not-found.png` });
continue;
}
const [downloadButton] = await page.$x(`//a[contains(.,\'Download Payslip\')]`);
if (!downloadButton) {
console.warn(`Download button not found`);
await page.screenshot({ path: `${year}-${name}-download-button-not-found.png` });
continue;
}
// download it
await downloadButton.click();
await page.waitForResponse(response => {
const isDownload = response.url().indexOf('api/myfinances/payslip/export/') !== -1;
return isDownload && response.status() === 200;
});
await page.waitFor(750);
}
}
console.log('success!');
await browser.close();
};
try {
download();
} catch (e) {
console.error(e);
}
@rheaditi
Copy link
Author

rheaditi commented Jun 21, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment